$npx -y skills add gooseworks-ai/goose-skills --skill media-proxyShared helper that routes ALL paid media generation (FAL image/video, ElevenLabs music) through the GooseWorks proxies so every call bills the Ads agent — never a provider SDK's default host. Host-swaps the FAL queue URLs, loads the agent token from ~/.gooseworks/credentials.json
| 1 | # media-proxy |
| 2 | |
| 3 | The foundation capability for paid media in the video-ad pipeline. It fixes the |
| 4 | auth-path conflict where engine scripts called FAL/ElevenLabs **directly** (billing |
| 5 | the wrong account): all paid calls now go through |
| 6 | `<api_base>/api/internal/{fal-proxy,elevenlabs-proxy}` with `?token=&agent_id=`, which |
| 7 | **bills the Ads agent**. |
| 8 | |
| 9 | ## Crash-resume (never lose / double-bill a paid render) |
| 10 | |
| 11 | A FAL submit BILLS immediately, but the local backend can blip during a multi-minute |
| 12 | render. Two built-in protections (automatic for every capability that imports this): |
| 13 | - **Poll-through-outage** — `_fal_run`'s poll loop re-attaches to the same status/result |
| 14 | URL through `connection refused` / timeout blips instead of crashing. |
| 15 | - **Persist + resume** — each submit's `request_id` + poll URLs are written to |
| 16 | `~/.gooseworks/pending-fal-jobs/`. If the poller still dies, **re-attach instead of |
| 17 | re-firing** (re-firing double-bills): `resume_fal(request_id)` in Python, or the CLI: |
| 18 | |
| 19 | ```bash |
| 20 | resume.py --list # resumable (submitted, unfinished) jobs |
| 21 | resume.py --request-id <id> --out final.mp4 # poll to completion + download |
| 22 | ``` |
| 23 | `resume_fal` NEVER re-submits, so it can't double-charge. |
| 24 | |
| 25 | ## Use it |
| 26 | |
| 27 | ```python |
| 28 | from media_proxy import fal_generate, fal_generate_video, eleven_music, download |
| 29 | |
| 30 | # image (nano-banana / gpt-image / etc.) — inputs must be PUBLIC urls |
| 31 | img = fal_generate("fal-ai/nano-banana/edit", |
| 32 | {"prompt": p, "image_urls": [product_url], "aspect_ratio": "9:16"}) |
| 33 | # video i2v (kling / seedance / veo) |
| 34 | vid = fal_generate_video("fal-ai/kling-video/v2.1/standard/image-to-video", |
| 35 | {"prompt": p, "image_url": keyframe_url, "duration": "10"}) |
| 36 | # music bed |
| 37 | eleven_music(prompt, 10500, "music.mp3", force_instrumental=True) |
| 38 | ``` |
| 39 | |
| 40 | ## Contracts (load-bearing) |
| 41 | |
| 42 | - **Bills the Ads agent** — `?token=&agent_id=` from `~/.gooseworks/credentials.json` |
| 43 | (the CLI writes it; run `gooseworks login` if missing). |
| 44 | - **Host-swap the FAL queue URLs** — submit returns `status_url`/`response_url` on |
| 45 | `queue.fal.run`; the helper rewrites them to the proxy base (keeps the path). Never |
| 46 | poll `queue.fal.run` directly (401 + burns credits). |
| 47 | - **FAL inputs that are local files must be PUBLIC urls.** The orchestrator hosts a |
| 48 | local image/audio via the MCP `get_upload_url` → `get_download_url` presigned URL and |
| 49 | passes THAT url in. This module does not do MCP uploads (prefer the presigned url; |
| 50 | `fal-storage-proxy` may 404). |
| 51 | - **Only the final `*.fal.media` url is a real public URL** — everything else is behind |
| 52 | the proxy. |
| 53 | |
| 54 | ## Related |
| 55 | - Used by `create-image-fal`, `create-video-fal`, `create-music-elevenlabs`. |
| 56 | - The `goose-video` orchestrator hosts local inputs (MCP upload → presign) before calling these. |