@officexapp/vidfarm-devcli 0.21.21 → 0.21.22

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -221,6 +221,7 @@ Choose the narrowest path that satisfies the request.
221
221
  2. If the user already knows the goal and needs a suitable template, read `references/core-workflows.md` and use the template discovery flow.
222
222
  3. If the task is “change this video,” read `references/editor-workflows.md`.
223
223
  4. If the task is “find footage” or “use our existing assets,” read `references/assets-and-sourcing.md`.
224
+ 4b. If the task is **“download this video/audio off a website”** (a pasted YouTube / TikTok / Instagram / X post URL the user wants the actual file from), Vidfarm does that for you on a **paid plan** — `POST /api/v1/primitives/videos/download` (or `/audio/download`), devcli `vidfarm download-video <url>` / `vidfarm download-audio <url>`. **Free plan → do not call it; walk the user through opening the URL in Chrome and downloading it from the page, then `vidfarm put-file` the local file in for $0.** Details in `references/assets-and-sourcing.md` → `references/primitives.md`.
224
225
  5. If the task is scripted, local, CI-driven, or `vidfarm serve`-based, read `references/automation-and-local-dev.md`.
225
226
  6. If the task explicitly asks for a primitive or needs specialized generation/transcription work, read `references/primitives.md`.
226
227
  7. If the task is the MARKETPLACE (ordering videos from specialist agents): browsing is web-only for paying customers — send the human to https://vidfarm.cc/marketplace, never render it locally. Placing/listing orders is the thin REST wrapper in `references/core-workflows.md` (§ Marketplace); anything deeper on a gig (inbox, proofs, payouts) needs the external Dollar Platoon skill — `npx skills add https://github.com/OfficeXApp/dollarplatoon-skill` — the same way FlockPoster work beyond scheduling needs `npx skills add https://github.com/OfficeXApp/flockposter-skill`.
@@ -1,3 +1,20 @@
1
+ ## Download a video from a website (Vidfarm fetches it for you — paid plans)
2
+
3
+ **Yes, Vidfarm can download videos off the web for the user.** When someone pastes a YouTube / TikTok / Instagram / X (Twitter) / other supported post URL and wants the actual file — "grab this video", "save this clip", "get me that audio" — you do **not** need yt-dlp, a browser extension, or an apology. One primitive does it and returns a durable Vidfarm-hosted asset:
4
+
5
+ | Want | Call | devcli |
6
+ | --- | --- | --- |
7
+ | The video (or a photo/carousel post → ordered slideshow) | `POST /api/v1/primitives/videos/download` `{ tracer, payload: { source_url, quality?: "best"\|"hd"\|"full_hd" } }` | `vidfarm download-video <url>` |
8
+ | Just the audio/music/voice track | `POST /api/v1/primitives/audio/download` `{ tracer, payload: { source_url } }` | `vidfarm download-audio <url>` |
9
+
10
+ Both are async primitive jobs — poll `GET /api/v1/primitives/jobs/:jobId` until `succeeded`, then read `primary_file_url`. Full field-level detail (slideshow response shape, billing, examples) lives in `references/primitives.md`.
11
+
12
+ **PAID PLANS ONLY.** These ride a paid third-party resolver billed to the wallet, so a free-plan customer gets `402 insufficient_funds`. Check the plan / cost mode before promising it.
13
+
14
+ **Free-plan users — the fallback is Chrome, and it works:** tell them (or, if you have browser automation, do it yourself) to **open the URL in Chrome and download the video from there** — right-click → *Save video as*, the downloads pane, DevTools → Network, or a Googled `"<platform> video downloader"` site in that same session. Then bring the local file in for free with `vidfarm put-file ./the-file.mp4 --folder <folder>` or the web uploader. Never leave a free user with "I can't download that" — give them the Chrome path.
15
+
16
+ **Don't reach for this when the real goal is clips.** If the user wants short clips mined out of a long video, `POST /raws/scan` with `source_url` already downloads the source for you as part of the hunt (below) — a separate download call is wasted spend.
17
+
1
18
  ## Raws (long-form → short-form raws)
2
19
 
3
20
  Mine a **long-form** video (podcast, stream VOD, webinar, any YouTube/TikTok/IG/X URL, or an upload) into a library of tagged, searchable **raws**. This is the `/raws` surface — browse it at `https://vidfarm.cc/library/raws` (the Library page's "Approved / Raws" tabs).
@@ -175,9 +175,11 @@ The licensed harness also carries the **generative build workflow** guidance (ch
175
175
  | `vidfarm wallet [--job <id>\|--tracer <t>] [--limit <n>]` | `GET /api/v1/user/me/wallet` | cost log: balance + lifetime spend + recent charges. `--job <renderJobId>` prints **what that one video cost** (sums its charges); `--tracer <t>` sums a tracer. Cloud-only, paid plan. Aliases: `spend`, `costs` |
176
176
  | `vidfarm provider-keys` / `vidfarm add-provider-key <p> <secret>` | `GET`·`POST /api/v1/user/me/provider-keys` | manage AI keys |
177
177
  | `vidfarm upload <file> [--folder <path>]` | presign → S3 PUT → finalize (`.../temporary-files/presign` + `.../temporary-files`) | upload → durable URL (ephemeral, 30-day TTL; prefer `--folder temp` for scratch). Goes direct to S3, so large files (up to **200 MB**) bypass the ~6 MB Lambda body limit |
178
- | `vidfarm download <url> [dest]` | (streams any URL to disk) | download media |
179
- | `vidfarm download-post <url> [--quality best\|hd\|full_hd]` | `POST /api/v1/primitives/videos/download` + poll | download a social/media post into a durable MP4 or slideshow |
180
- | `vidfarm download-post-audio <url>` | `POST /api/v1/primitives/audio/download` + poll | download a social/media post's audio into a durable audio file |
178
+ | `vidfarm download <url> [dest]` | (streams any URL to disk) | download media from a **direct** media URL. Free — no plan, no job |
179
+ | `vidfarm download-video <url> [--quality best\|hd\|full_hd]` | `POST /api/v1/primitives/videos/download` + poll | **download a video FROM A WEBSITE** (YouTube/TikTok/IG/X/other supported posts) into durable Vidfarm media; photo/carousel posts return an ordered slideshow. **PAID PLAN** (wallet-billed resolver; free plans get 402). Aliases: `download-post`, `download-url` |
180
+ | `vidfarm download-audio <url>` | `POST /api/v1/primitives/audio/download` + poll | download just the AUDIO track (music/voice) off a website post into a durable audio file. **PAID PLAN**. Alias: `download-post-audio` |
181
+
182
+ > **Free plan / `cost-mode minimize`:** both download commands are gated by `guardBilled` and will refuse or warn. The free path is **Chrome**: open the URL in a real browser, save the file off the page (right-click → *Save video as*, downloads pane, or a Googled `"<platform> video downloader"` site), then `vidfarm put-file ./the-file.mp4 --folder <folder>` to bring it in for $0. The CLI prints these steps on a 402. Never tell a free user the video simply can't be downloaded.
181
183
  | `vidfarm files [--folder <path>]` | `GET /api/v1/user/me/attachments` | list My Files assets + folders |
182
184
  | `vidfarm files --search "…" [--folder <path>]` | `POST /api/v1/user/me/attachments/search` | find My Files assets by MEANING (keyword + vector over name/folder/notes) |
183
185
  | `vidfarm get-file <id> [dest] [--print]` | (resolve id → view_url, then stream/print) | read one My Files asset |
@@ -51,9 +51,11 @@ curl -X POST "$VIDFARM_BASE/api/v1/primitives/images/create-overlay" \
51
51
  -d '{"tracer": "vox-overlay", "payload": {"prompt": "a friendly cartoon lightbulb mascot, flat vector illustration", "aspect_ratio": "1:1"}}'
52
52
  ```
53
53
 
54
- ## Primitive: videos/download
54
+ ## Primitive: videos/download — **Vidfarm downloads videos off websites for you**
55
55
 
56
- Download a supported social/media post URL into a durable Vidfarm-hosted VISUAL asset. This route is intentionally broader than its old name suggests: it returns either a normal MP4 for video posts, or a slideshow payload for photo/carousel posts.
56
+ **Capability statement (say this plainly when a user asks "can you download this video?"): yes — on a paid plan, Vidfarm downloads a video straight off a website URL for you.** Hand it a YouTube / TikTok / Instagram / X (Twitter) / other supported post URL and it returns a durable Vidfarm-hosted file. No local yt-dlp, no browser extension, no "I can't access that URL." The route is intentionally broader than its old name suggests: it returns either a normal MP4 for video posts, or a slideshow payload for photo/carousel posts.
57
+
58
+ **Paid-only, and that gate is real.** The resolver is a paid RapidAPI pass-through billed to the wallet, so a free-plan customer gets `402 insufficient_funds`. Never promise this to a free user — route them to the browser fallback below instead.
57
59
 
58
60
  - `POST /api/v1/primitives/videos/download`
59
61
  - Body: `{ "tracer": "...", "payload": { "source_url": "https://...", "quality"?: "best" | "hd" | "full_hd", "save_manifest"?: true }, "webhook_url"?: "..." }`
@@ -61,8 +63,20 @@ Download a supported social/media post URL into a durable Vidfarm-hosted VISUAL
61
63
  - Video post: read `primary_file_url` / `video.file_url` / `videoUrl` for the durable MP4.
62
64
  - Photo/carousel post: read `mediaKind: "slideshow"`, ordered `slideImageUrls[]`, optional `slideAudioUrl`, and `primary_file_url` (first slide image).
63
65
  - Billing: RapidAPI pass-through wallet charge (`rapidapi_video_download`) plus the small mirror/extract pass (`video_download_lambda` when MP4 download is involved).
64
- - Free-plan / no-spend rule: do **not** use this paid route when the user wants a free path. In the web app, tell them to use the browser to find a downloader instead. Fallback wording is explicit: suggest Googling `"youtube video downloader"` or `"tiktok/twitter/instagram/etc audio/video downloader"`.
65
- - devcli wrapper: `vidfarm download-post <url> [--quality best|hd|full_hd]`
66
+ - devcli wrapper: `vidfarm download-video <url> [--quality best|hd|full_hd]` (aliases: `download-post`, `download-url`)
67
+
68
+ ### FREE-PLAN FALLBACK — drive Chrome instead (never dead-end the user)
69
+
70
+ If the user is on the free plan, says "don't spend money", or cost mode is `minimize`, do **not** call this route. **Load the URL in Chrome and download it by that means** — it costs $0.00 and needs no Vidfarm plan:
71
+
72
+ 1. Open the post/page in Chrome. A real browser session carries the login/consent/age walls that a bare `fetch` can't.
73
+ 2. Save the video off the page — right-click → *Save video as*, the browser's downloads pane, or DevTools → Network → the media response.
74
+ 3. If the page won't hand it over, Google `"youtube video downloader"` (or `"tiktok/instagram/x/twitter video downloader"`) and use one of those sites in the same Chrome session.
75
+ 4. Bring the resulting local file into Vidfarm for free: `vidfarm put-file ./the-file.mp4 --folder <folder>` (devcli) or the web app's upload. From there it's a normal durable asset.
76
+
77
+ If you're a desktop coding agent with browser automation (Playwright/Puppeteer/CDP, or the user's own Chrome), you can perform steps 1–3 yourself. If you're the web copilot with no browser tool, hand the user these steps — do not claim you downloaded it.
78
+
79
+ Say the tradeoff out loud once: *"Downloading it for you is a paid-plan feature; on the free plan I'll walk you through grabbing it in Chrome instead."*
66
80
 
67
81
  Example:
68
82
 
@@ -81,9 +95,9 @@ Download just the original audio/music/voice track from a supported social/media
81
95
  - Body: `{ "tracer": "...", "payload": { "source_url": "https://...", "save_manifest"?: true }, "webhook_url"?: "..." }`
82
96
  - Response: standard primitive job. Poll `GET /api/v1/primitives/jobs/:jobId` until `status: "succeeded"`, then read `primary_file_url` / `audio.file_url` / `audioUrl`.
83
97
  - Behavior: prefers the provider's direct audio track when present; otherwise resolves the source video and extracts audio server-side.
84
- - Billing: same RapidAPI wallet class as `videos/download`.
85
- - Free-plan / no-spend rule: same as the visual download route. Use the browser and, if needed, suggest Googling `"youtube audio downloader"` or `"tiktok/twitter/instagram/etc audio/video downloader"` instead of spending wallet credits.
86
- - devcli wrapper: `vidfarm download-post-audio <url>`
98
+ - Billing: same RapidAPI wallet class as `videos/download` — **paid plans only**; free plans get `402`.
99
+ - Free-plan / no-spend rule: identical to the visual route **load the URL in Chrome and download from there**, then `vidfarm put-file` the result. Google `"youtube audio downloader"` or `"tiktok/twitter/instagram/etc audio downloader"` if the page won't release the track. See the FREE-PLAN FALLBACK block above.
100
+ - devcli wrapper: `vidfarm download-audio <url>` (alias: `download-post-audio`)
87
101
 
88
102
  Example:
89
103
 
package/SKILL.director.md CHANGED
@@ -221,6 +221,7 @@ Choose the narrowest path that satisfies the request.
221
221
  2. If the user already knows the goal and needs a suitable template, read `references/core-workflows.md` and use the template discovery flow.
222
222
  3. If the task is “change this video,” read `references/editor-workflows.md`.
223
223
  4. If the task is “find footage” or “use our existing assets,” read `references/assets-and-sourcing.md`.
224
+ 4b. If the task is **“download this video/audio off a website”** (a pasted YouTube / TikTok / Instagram / X post URL the user wants the actual file from), Vidfarm does that for you on a **paid plan** — `POST /api/v1/primitives/videos/download` (or `/audio/download`), devcli `vidfarm download-video <url>` / `vidfarm download-audio <url>`. **Free plan → do not call it; walk the user through opening the URL in Chrome and downloading it from the page, then `vidfarm put-file` the local file in for $0.** Details in `references/assets-and-sourcing.md` → `references/primitives.md`.
224
225
  5. If the task is scripted, local, CI-driven, or `vidfarm serve`-based, read `references/automation-and-local-dev.md`.
225
226
  6. If the task explicitly asks for a primitive or needs specialized generation/transcription work, read `references/primitives.md`.
226
227
  7. If the task is the MARKETPLACE (ordering videos from specialist agents): browsing is web-only for paying customers — send the human to https://vidfarm.cc/marketplace, never render it locally. Placing/listing orders is the thin REST wrapper in `references/core-workflows.md` (§ Marketplace); anything deeper on a gig (inbox, proofs, payouts) needs the external Dollar Platoon skill — `npx skills add https://github.com/OfficeXApp/dollarplatoon-skill` — the same way FlockPoster work beyond scheduling needs `npx skills add https://github.com/OfficeXApp/flockposter-skill`.
@@ -1020,6 +1021,23 @@ For anything bespoke, pass raw stops: `vidfarm keyframes ./work --layer robot --
1020
1021
 
1021
1022
  **Pick the store by lifetime.** A throwaway intermediate (cloud render source, a `place` scratch asset) belongs in the 30-day `temp/` store: `vidfarm upload clip.mp4 --folder temp`, `vidfarm place … --folder temp` — quarantined in one place you can periodically purge. Anything you **approve/share is permanent**, so `vidfarm approve --video ./final.mp4` uploads to **durable My Files** by default (the share video would otherwise 404 after the temp store's 30-day deletion). Only add `vidfarm approve --temp` when you deliberately want a disposable 30-day preview.
1022
1023
 
1024
+ ## Download a video from a website (Vidfarm fetches it for you — paid plans)
1025
+
1026
+ **Yes, Vidfarm can download videos off the web for the user.** When someone pastes a YouTube / TikTok / Instagram / X (Twitter) / other supported post URL and wants the actual file — "grab this video", "save this clip", "get me that audio" — you do **not** need yt-dlp, a browser extension, or an apology. One primitive does it and returns a durable Vidfarm-hosted asset:
1027
+
1028
+ | Want | Call | devcli |
1029
+ | --- | --- | --- |
1030
+ | The video (or a photo/carousel post → ordered slideshow) | `POST /api/v1/primitives/videos/download` `{ tracer, payload: { source_url, quality?: "best"\|"hd"\|"full_hd" } }` | `vidfarm download-video <url>` |
1031
+ | Just the audio/music/voice track | `POST /api/v1/primitives/audio/download` `{ tracer, payload: { source_url } }` | `vidfarm download-audio <url>` |
1032
+
1033
+ Both are async primitive jobs — poll `GET /api/v1/primitives/jobs/:jobId` until `succeeded`, then read `primary_file_url`. Full field-level detail (slideshow response shape, billing, examples) lives in `references/primitives.md`.
1034
+
1035
+ **PAID PLANS ONLY.** These ride a paid third-party resolver billed to the wallet, so a free-plan customer gets `402 insufficient_funds`. Check the plan / cost mode before promising it.
1036
+
1037
+ **Free-plan users — the fallback is Chrome, and it works:** tell them (or, if you have browser automation, do it yourself) to **open the URL in Chrome and download the video from there** — right-click → *Save video as*, the downloads pane, DevTools → Network, or a Googled `"<platform> video downloader"` site in that same session. Then bring the local file in for free with `vidfarm put-file ./the-file.mp4 --folder <folder>` or the web uploader. Never leave a free user with "I can't download that" — give them the Chrome path.
1038
+
1039
+ **Don't reach for this when the real goal is clips.** If the user wants short clips mined out of a long video, `POST /raws/scan` with `source_url` already downloads the source for you as part of the hunt (below) — a separate download call is wasted spend.
1040
+
1023
1041
  ## Raws (long-form → short-form raws)
1024
1042
 
1025
1043
  Mine a **long-form** video (podcast, stream VOD, webinar, any YouTube/TikTok/IG/X URL, or an upload) into a library of tagged, searchable **raws**. This is the `/raws` surface — browse it at `https://vidfarm.cc/library/raws` (the Library page's "Approved / Raws" tabs).
@@ -1321,9 +1339,11 @@ The licensed harness also carries the **generative build workflow** guidance (ch
1321
1339
  | `vidfarm wallet [--job <id>\|--tracer <t>] [--limit <n>]` | `GET /api/v1/user/me/wallet` | cost log: balance + lifetime spend + recent charges. `--job <renderJobId>` prints **what that one video cost** (sums its charges); `--tracer <t>` sums a tracer. Cloud-only, paid plan. Aliases: `spend`, `costs` |
1322
1340
  | `vidfarm provider-keys` / `vidfarm add-provider-key <p> <secret>` | `GET`·`POST /api/v1/user/me/provider-keys` | manage AI keys |
1323
1341
  | `vidfarm upload <file> [--folder <path>]` | presign → S3 PUT → finalize (`.../temporary-files/presign` + `.../temporary-files`) | upload → durable URL (ephemeral, 30-day TTL; prefer `--folder temp` for scratch). Goes direct to S3, so large files (up to **200 MB**) bypass the ~6 MB Lambda body limit |
1324
- | `vidfarm download <url> [dest]` | (streams any URL to disk) | download media |
1325
- | `vidfarm download-post <url> [--quality best\|hd\|full_hd]` | `POST /api/v1/primitives/videos/download` + poll | download a social/media post into a durable MP4 or slideshow |
1326
- | `vidfarm download-post-audio <url>` | `POST /api/v1/primitives/audio/download` + poll | download a social/media post's audio into a durable audio file |
1342
+ | `vidfarm download <url> [dest]` | (streams any URL to disk) | download media from a **direct** media URL. Free — no plan, no job |
1343
+ | `vidfarm download-video <url> [--quality best\|hd\|full_hd]` | `POST /api/v1/primitives/videos/download` + poll | **download a video FROM A WEBSITE** (YouTube/TikTok/IG/X/other supported posts) into durable Vidfarm media; photo/carousel posts return an ordered slideshow. **PAID PLAN** (wallet-billed resolver; free plans get 402). Aliases: `download-post`, `download-url` |
1344
+ | `vidfarm download-audio <url>` | `POST /api/v1/primitives/audio/download` + poll | download just the AUDIO track (music/voice) off a website post into a durable audio file. **PAID PLAN**. Alias: `download-post-audio` |
1345
+
1346
+ > **Free plan / `cost-mode minimize`:** both download commands are gated by `guardBilled` and will refuse or warn. The free path is **Chrome**: open the URL in a real browser, save the file off the page (right-click → *Save video as*, downloads pane, or a Googled `"<platform> video downloader"` site), then `vidfarm put-file ./the-file.mp4 --folder <folder>` to bring it in for $0. The CLI prints these steps on a 402. Never tell a free user the video simply can't be downloaded.
1327
1347
  | `vidfarm files [--folder <path>]` | `GET /api/v1/user/me/attachments` | list My Files assets + folders |
1328
1348
  | `vidfarm files --search "…" [--folder <path>]` | `POST /api/v1/user/me/attachments/search` | find My Files assets by MEANING (keyword + vector over name/folder/notes) |
1329
1349
  | `vidfarm get-file <id> [dest] [--print]` | (resolve id → view_url, then stream/print) | read one My Files asset |
@@ -1556,9 +1576,11 @@ curl -X POST "$VIDFARM_BASE/api/v1/primitives/images/create-overlay" \
1556
1576
  -d '{"tracer": "vox-overlay", "payload": {"prompt": "a friendly cartoon lightbulb mascot, flat vector illustration", "aspect_ratio": "1:1"}}'
1557
1577
  ```
1558
1578
 
1559
- ## Primitive: videos/download
1579
+ ## Primitive: videos/download — **Vidfarm downloads videos off websites for you**
1580
+
1581
+ **Capability statement (say this plainly when a user asks "can you download this video?"): yes — on a paid plan, Vidfarm downloads a video straight off a website URL for you.** Hand it a YouTube / TikTok / Instagram / X (Twitter) / other supported post URL and it returns a durable Vidfarm-hosted file. No local yt-dlp, no browser extension, no "I can't access that URL." The route is intentionally broader than its old name suggests: it returns either a normal MP4 for video posts, or a slideshow payload for photo/carousel posts.
1560
1582
 
1561
- Download a supported social/media post URL into a durable Vidfarm-hosted VISUAL asset. This route is intentionally broader than its old name suggests: it returns either a normal MP4 for video posts, or a slideshow payload for photo/carousel posts.
1583
+ **Paid-only, and that gate is real.** The resolver is a paid RapidAPI pass-through billed to the wallet, so a free-plan customer gets `402 insufficient_funds`. Never promise this to a free user route them to the browser fallback below instead.
1562
1584
 
1563
1585
  - `POST /api/v1/primitives/videos/download`
1564
1586
  - Body: `{ "tracer": "...", "payload": { "source_url": "https://...", "quality"?: "best" | "hd" | "full_hd", "save_manifest"?: true }, "webhook_url"?: "..." }`
@@ -1566,8 +1588,20 @@ Download a supported social/media post URL into a durable Vidfarm-hosted VISUAL
1566
1588
  - Video post: read `primary_file_url` / `video.file_url` / `videoUrl` for the durable MP4.
1567
1589
  - Photo/carousel post: read `mediaKind: "slideshow"`, ordered `slideImageUrls[]`, optional `slideAudioUrl`, and `primary_file_url` (first slide image).
1568
1590
  - Billing: RapidAPI pass-through wallet charge (`rapidapi_video_download`) plus the small mirror/extract pass (`video_download_lambda` when MP4 download is involved).
1569
- - Free-plan / no-spend rule: do **not** use this paid route when the user wants a free path. In the web app, tell them to use the browser to find a downloader instead. Fallback wording is explicit: suggest Googling `"youtube video downloader"` or `"tiktok/twitter/instagram/etc audio/video downloader"`.
1570
- - devcli wrapper: `vidfarm download-post <url> [--quality best|hd|full_hd]`
1591
+ - devcli wrapper: `vidfarm download-video <url> [--quality best|hd|full_hd]` (aliases: `download-post`, `download-url`)
1592
+
1593
+ ### FREE-PLAN FALLBACK — drive Chrome instead (never dead-end the user)
1594
+
1595
+ If the user is on the free plan, says "don't spend money", or cost mode is `minimize`, do **not** call this route. **Load the URL in Chrome and download it by that means** — it costs $0.00 and needs no Vidfarm plan:
1596
+
1597
+ 1. Open the post/page in Chrome. A real browser session carries the login/consent/age walls that a bare `fetch` can't.
1598
+ 2. Save the video off the page — right-click → *Save video as*, the browser's downloads pane, or DevTools → Network → the media response.
1599
+ 3. If the page won't hand it over, Google `"youtube video downloader"` (or `"tiktok/instagram/x/twitter video downloader"`) and use one of those sites in the same Chrome session.
1600
+ 4. Bring the resulting local file into Vidfarm for free: `vidfarm put-file ./the-file.mp4 --folder <folder>` (devcli) or the web app's upload. From there it's a normal durable asset.
1601
+
1602
+ If you're a desktop coding agent with browser automation (Playwright/Puppeteer/CDP, or the user's own Chrome), you can perform steps 1–3 yourself. If you're the web copilot with no browser tool, hand the user these steps — do not claim you downloaded it.
1603
+
1604
+ Say the tradeoff out loud once: *"Downloading it for you is a paid-plan feature; on the free plan I'll walk you through grabbing it in Chrome instead."*
1571
1605
 
1572
1606
  Example:
1573
1607
 
@@ -1586,9 +1620,9 @@ Download just the original audio/music/voice track from a supported social/media
1586
1620
  - Body: `{ "tracer": "...", "payload": { "source_url": "https://...", "save_manifest"?: true }, "webhook_url"?: "..." }`
1587
1621
  - Response: standard primitive job. Poll `GET /api/v1/primitives/jobs/:jobId` until `status: "succeeded"`, then read `primary_file_url` / `audio.file_url` / `audioUrl`.
1588
1622
  - Behavior: prefers the provider's direct audio track when present; otherwise resolves the source video and extracts audio server-side.
1589
- - Billing: same RapidAPI wallet class as `videos/download`.
1590
- - Free-plan / no-spend rule: same as the visual download route. Use the browser and, if needed, suggest Googling `"youtube audio downloader"` or `"tiktok/twitter/instagram/etc audio/video downloader"` instead of spending wallet credits.
1591
- - devcli wrapper: `vidfarm download-post-audio <url>`
1623
+ - Billing: same RapidAPI wallet class as `videos/download` — **paid plans only**; free plans get `402`.
1624
+ - Free-plan / no-spend rule: identical to the visual route **load the URL in Chrome and download from there**, then `vidfarm put-file` the result. Google `"youtube audio downloader"` or `"tiktok/twitter/instagram/etc audio downloader"` if the page won't release the track. See the FREE-PLAN FALLBACK block above.
1625
+ - devcli wrapper: `vidfarm download-audio <url>` (alias: `download-post-audio`)
1592
1626
 
1593
1627
  Example:
1594
1628
 
package/SKILL.md CHANGED
@@ -73,6 +73,7 @@ For composition *authoring* craft (motion, keyframes, scene design), Vidfarm shi
73
73
  2. "Find a template and start" → `references/core-workflows.md`
74
74
  3. "Change / re-theme this video" → `references/editor-workflows.md`
75
75
  4. "Find footage / use our assets" → `references/assets-and-sourcing.md`
76
+ 4b. **"Download this video/audio from <a website URL>"** → Vidfarm fetches it for you on a **paid plan**: `POST /api/v1/primitives/videos/download` (or `/audio/download`); devcli `vidfarm download-video <url>` / `vidfarm download-audio <url>`. Works on YouTube, TikTok, Instagram, X, and other supported posts; returns a durable Vidfarm file (photo/carousel posts → an ordered slideshow). **Free plan gets a 402 — don't call it. Tell the user (or, with browser automation, do it yourself) to open the URL in Chrome and download it from the page, then `vidfarm put-file ./the-file.mp4` to bring it in for $0.** Never answer "I can't download that." Details: `references/assets-and-sourcing.md`.
76
77
  5. "Script / batch / render loop" → `references/automation-and-local-dev.md`
77
78
  6. "I need TTS / music / captions / background removal" → `references/primitives.md`
78
79
 
package/dist/src/cli.js CHANGED
@@ -600,12 +600,19 @@ Files (multi-step flows the devcli handles for you):
600
600
  --notes <text> Metadata notes (what it is / when to use it) — vector-embedded for search
601
601
  e.g. vidfarm put-file About.md --folder acme-skincare
602
602
  echo "..." | vidfarm put-file --stdin --as About.md --folder acme
603
- download <url> [dest] Stream any Vidfarm/media URL to disk
604
- download-post <url> Download a social/media post into Vidfarm media POST /api/v1/primitives/videos/download
603
+ download <url> [dest] Stream any Vidfarm/media URL to disk (free — direct media URLs only)
604
+ download-video <url> PAID. Download a video FROM A WEBSITE (YouTube, TikTok, Instagram,
605
+ (aka download-post) X/Twitter, and other supported posts) into durable Vidfarm media.
606
+ Photo/carousel posts come back as an ordered slideshow.
607
+ → POST /api/v1/primitives/videos/download
605
608
  --quality <q> best | hd | full_hd (default: best)
606
609
  --no-wait Return the queued job immediately instead of polling
607
- download-post-audio <url> Download a social/media post's audio into Vidfarm POST /api/v1/primitives/audio/download
610
+ download-audio <url> PAID. Download just the AUDIO track (music/voice) from a website post
611
+ (aka download-post-audio) → POST /api/v1/primitives/audio/download
608
612
  --no-wait Return the queued job immediately instead of polling
613
+ FREE PLAN: both bill the wallet via a paid resolver. If the user is
614
+ free/no-spend, don't call them — open the URL in Chrome and download
615
+ it from the page, then 'vidfarm put-file ./the-file' to bring it in.
609
616
  tasks Cloud Status: imports · forks · renders · jobs → GET /api/v1/user/me/tasks
610
617
  --running|--done|--error Filter (or --status <all|running|done|error>); --limit <n>
611
618
  wallet Wallet cost log: balance + what each video cost → GET /api/v1/user/me/wallet
@@ -769,7 +776,10 @@ function loadDotEnvCredentials(cwd = process.cwd()) {
769
776
  async function main() {
770
777
  const argv = process.argv.slice(2);
771
778
  loadDotEnvCredentials();
772
- if (argv.length === 0 || argv[0] === "--help" || argv[0] === "-h") {
779
+ // `help` must be caught here too: without it the bare word falls through the
780
+ // dispatch switch and is treated as a TEMPLATE ID by the default `serve` case,
781
+ // which boots a local server on :3000 instead of printing help.
782
+ if (argv.length === 0 || argv[0] === "help" || argv[0] === "--help" || argv[0] === "-h") {
773
783
  process.stdout.write(HELP);
774
784
  return;
775
785
  }
@@ -952,10 +962,14 @@ async function main() {
952
962
  case "download":
953
963
  await runDownloadCommand(rest);
954
964
  return;
965
+ // Aliases: agents look for "download a video from a website", not "post".
955
966
  case "download-post":
967
+ case "download-video":
968
+ case "download-url":
956
969
  await runDownloadPostCommand(rest);
957
970
  return;
958
971
  case "download-post-audio":
972
+ case "download-audio":
959
973
  await runDownloadPostAudioCommand(rest);
960
974
  return;
961
975
  case "files":
@@ -6877,6 +6891,27 @@ async function runDownloadCommand(argv) {
6877
6891
  console.log(`${GREEN}Downloaded ${formatBytes(bytes)} → ${dest}${RESET}`);
6878
6892
  }
6879
6893
  }
6894
+ // The FREE fallback for the paid download primitives. Both routes ride a paid
6895
+ // RapidAPI resolver billed to the wallet, so a free-plan customer gets a 402.
6896
+ // Rather than dead-ending them, tell the agent to drive the user's own browser:
6897
+ // open the URL in Chrome and download it from there (no Vidfarm spend).
6898
+ // NOTE: a hoisted function, NOT a module const — `void main()` runs synchronously
6899
+ // until its first await, so a const declared this far down the file is still in
6900
+ // its TDZ when a command handler reads it (same gotcha as the *_HELP consts).
6901
+ function downloadFreeAlternative() {
6902
+ return "open the URL in Chrome and download it from the page yourself ($0.00 — no wallet spend)";
6903
+ }
6904
+ function printDownloadFreePathHint(ctx, kind) {
6905
+ if (ctx.json)
6906
+ return;
6907
+ const noun = kind === "video" ? "video" : "audio";
6908
+ console.log("");
6909
+ console.log(`${YELLOW}Free path (no Vidfarm spend):${RESET} load the URL in Chrome and grab the ${noun} from there.`);
6910
+ console.log(`${DIM} 1. Open the post/page in Chrome (a real browser session handles the login/consent walls).${RESET}`);
6911
+ console.log(`${DIM} 2. Save the ${noun} from the page — right-click → Save, the browser's downloads, or a downloader site.${RESET}`);
6912
+ console.log(`${DIM} 3. Google "youtube ${noun} downloader" or "tiktok/instagram/x ${noun} downloader" if the page won't give it up.${RESET}`);
6913
+ console.log(`${DIM} 4. Then bring the local file into Vidfarm for free: vidfarm put-file ./the-file --folder <folder>${RESET}`);
6914
+ }
6880
6915
  async function runDownloadPostCommand(argv) {
6881
6916
  const parsed = parseArgs({
6882
6917
  args: argv,
@@ -6891,6 +6926,11 @@ async function runDownloadPostCommand(argv) {
6891
6926
  throw new Error("download-post --quality must be one of: best, hd, full_hd.");
6892
6927
  }
6893
6928
  const ctx = commonContext(parsed.values);
6929
+ guardBilled(ctx, {
6930
+ label: "download a video from a website (paid plan — RapidAPI resolver)",
6931
+ estimate: "small per-download wallet charge",
6932
+ freeAlternative: downloadFreeAlternative()
6933
+ });
6894
6934
  const tracer = String(parsed.values.tracer ?? `devcli-download-post-${Date.now().toString(36)}`);
6895
6935
  const submit = await apiRequest({
6896
6936
  method: "POST",
@@ -6899,6 +6939,9 @@ async function runDownloadPostCommand(argv) {
6899
6939
  auth: ctx.auth,
6900
6940
  body: { tracer, payload: { source_url: url, quality } }
6901
6941
  });
6942
+ if (!submit.ok && (submit.status === 402 || submit.status === 401 || submit.status === 403)) {
6943
+ printDownloadFreePathHint(ctx, "video");
6944
+ }
6902
6945
  assertApiOk(submit, "download-post");
6903
6946
  const jobId = submit.json?.job_id;
6904
6947
  if (!jobId || parsed.values["no-wait"]) {
@@ -6920,6 +6963,11 @@ async function runDownloadPostAudioCommand(argv) {
6920
6963
  if (!url)
6921
6964
  throw new Error("download-post-audio requires a source URL.");
6922
6965
  const ctx = commonContext(parsed.values);
6966
+ guardBilled(ctx, {
6967
+ label: "download audio from a website (paid plan — RapidAPI resolver)",
6968
+ estimate: "small per-download wallet charge",
6969
+ freeAlternative: downloadFreeAlternative()
6970
+ });
6923
6971
  const tracer = String(parsed.values.tracer ?? `devcli-download-post-audio-${Date.now().toString(36)}`);
6924
6972
  const submit = await apiRequest({
6925
6973
  method: "POST",
@@ -6928,6 +6976,9 @@ async function runDownloadPostAudioCommand(argv) {
6928
6976
  auth: ctx.auth,
6929
6977
  body: { tracer, payload: { source_url: url } }
6930
6978
  });
6979
+ if (!submit.ok && (submit.status === 402 || submit.status === 401 || submit.status === 403)) {
6980
+ printDownloadFreePathHint(ctx, "audio");
6981
+ }
6931
6982
  assertApiOk(submit, "download-post-audio");
6932
6983
  const jobId = submit.json?.job_id;
6933
6984
  if (!jobId || parsed.values["no-wait"]) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@officexapp/vidfarm-devcli",
3
- "version": "0.21.21",
3
+ "version": "0.21.22",
4
4
  "description": "Local bridge for the Vidfarm Trackpad Editor. `vidfarm serve <template_id>` boots the FULL editor on localhost (disk-backed records/storage, free in-process render); edit composition.html on disk (Claude Code, Codex, etc.) and the browser live-morphs it.",
5
5
  "type": "module",
6
6
  "bin": {