@officexapp/vidfarm-devcli 0.21.23 → 0.21.26
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.
- package/.agents/skills/vidfarm/SKILL.md +14 -5
- package/.agents/skills/vidfarm/references/automation-and-local-dev.md +24 -4
- package/.agents/skills/vidfarm/references/core-workflows.md +1 -1
- package/.agents/skills/vidfarm-media/SKILL.md +3 -2
- package/.agents/skills/vidfarm-media/references/tts.md +1 -0
- package/SKILL.director.md +39 -10
- package/SKILL.md +11 -1
- package/clipper.md +34 -8
- package/dist/src/cli.js +90 -12
- package/dist/src/devcli/cost-mode.js +28 -16
- package/package.json +3 -2
- package/update.md +143 -0
|
@@ -81,15 +81,24 @@ Do not use this skill to author new templates from scratch, deploy platform infr
|
|
|
81
81
|
|
|
82
82
|
## Cost mode — ask before you spend, then remember it
|
|
83
83
|
|
|
84
|
-
Vidfarm work can burn real AI credits on the user's wallet / provider keys. **Save them money by default.** Before the first billed step of a session, ask the user which spend posture they want, and
|
|
84
|
+
Vidfarm work can burn real AI credits on the user's wallet / provider keys. **Save them money by default.** Before the first billed step of a session, ask the user which spend posture they want, and quote the per-video cost in one plain line each:
|
|
85
85
|
|
|
86
|
-
|
|
86
|
+
| Mode | Cost per video | What it means |
|
|
87
|
+
|---|---|---|
|
|
88
|
+
| **minimize** | **$0 — explicitly free** | free local compute + free stock media only; billed AI is refused without `--yes` |
|
|
89
|
+
| **hybrid** *(recommended)* | **~$0.01–$1** | free where it's free; pay AI only where it clearly wins |
|
|
90
|
+
| **rich-ai** | **$1+** | best quality; AI image/video/voice/music used freely |
|
|
91
|
+
|
|
92
|
+
**Every dollar above is billed to the user's OWN AI provider keys (BYOK)** — the keys they save via `vidfarm add-provider-key <provider> <key>` or **Settings → Bring your own keys** (<https://vidfarm.cc/settings/developer>). Vidfarm isn't marking it up; the model providers charge it directly. (Vidfarm wallet credits are only spent when the user deliberately runs on the platform key instead of their own.) Say this out loud the first time you ask — it's why `minimize` really is $0: with no key spend, nothing can be charged.
|
|
93
|
+
|
|
94
|
+
- **minimize** — **$0 videos.** Stay on FREE local compute wherever possible (local render, local TTS — `vidfarm tts` already defaults to the free local Kokoro voice in this mode — `stt --engine whisper`, `remove-greenscreen --local`, reused raw clips + HTML hyperframes). For assets, reach for the **free stock catalog** before paying to generate anything — `vidfarm media search "<meaning>" --type <bgm|sfx|image|vector|icon|video>` pulls royalty-free, commercial-safe music/sound-effects/images/icons/stock-video from pixabay/openverse/iconify at $0 instead of billing AI music/image generation (see the `vidfarm-media` skill). No surprise AI spend.
|
|
87
95
|
- **Check the keyless sources first — Openverse and iconify.** Openverse (CC/CC0 **music, SFX, and images**) and iconify (**icons**) need **no account or key at all**, so they always work in `minimize` mode. Prefer them for BGM, sound effects, icons, and CC imagery before anything else.
|
|
88
96
|
- **Pixabay key** unlocks the photos/vectors/stock-video slots (music/SFX/icons/CC images are keyless). It's a **free** stock-media key, not an AI key. Don't assume it's missing when a search comes up short — it **may already be saved**: check `vidfarm provider-keys` (or the web app's **Settings → Bring your own keys** / <https://vidfarm.cc/settings/developer>). If it isn't, the user grabs a free one at <https://pixabay.com/api/docs/> and saves it once — `vidfarm add-provider-key pixabay <key>`, the Settings surface, or by handing the key to their desktop AI agent to run that command. After it's saved, cost-mode `minimize` sourcing works end-to-end at $0.
|
|
89
97
|
- **You can still get CUSTOM art in `minimize` — hand the prompt to the user and let a free image generator do it.** Stock and `mask` only cover art that already exists somewhere; when the video genuinely needs a bespoke graphic, **don't conclude "we can't" and don't quietly bill `generate`**. Write the prompt and ask the user to paste it into a **free** image generator — <https://meta.ai>, free-tier ChatGPT, or a free Hugging Face image Space (<https://huggingface.co/spaces>) — then hand the PNG back with `vidfarm put-file` (or drag it into **My Files** in the web app). $0, zero wallet spend. Full loop + the prompt template: **“Free manual image-gen”** below.
|
|
90
|
-
- **hybrid** *(recommend this)* —
|
|
91
|
-
- **
|
|
92
|
-
- **
|
|
98
|
+
- **hybrid** *(recommend this)* — **~$0.01–$1 per video, on their BYOK key.** Free where it's free; pay for AI only where it clearly wins (a hero shot, a voice you can't fake locally). A mostly-hyperframes video with one generated image lands near the low end; a few AI images plus premium narration approaches the high end.
|
|
99
|
+
- **Narration starts free here too.** In BOTH `minimize` and `hybrid`, a bare `vidfarm tts "…"` runs the keyless local Kokoro-82M voice — no flag needed. Ask for a premium voice explicitly (`--style`, `--voice <provider preset>`, `--provider`, `--own-key`, or `--cloud`) when the piece needs one, and say so out loud so the user knows what they're paying for. `rich-ai` skips the free default entirely. If the local engine isn't installed on their machine, the run falls back to their provider key / cloud automatically and prints why.
|
|
100
|
+
- **Free-TTS draft, premium-TTS final — now the default shape, so SAY it out loud.** Narration gets rewritten several times before the script locks, and paying for premium ElevenLabs TTS on every draft wastes money re-voicing lines that will change. The CLI defaults to the free local voice in `hybrid`, so drafts are free automatically — but a robotic draft voice reads as "broken" to a user who wasn't told. **Tell them the first time you generate narration**: the draft voice is the free local one, and you'll re-voice the approved final script in premium TTS (`--cloud` ElevenLabs, or `--style`/`--voice` on their own key) right before the last render. If they'd rather hear the real voice the whole way, pass the premium flags from the start — and if the piece is voice-led (a testimonial, a brand spot where the voice IS the product), offer premium up front instead of drafting free.
|
|
101
|
+
- **rich-ai** — **$1+ per video, on their BYOK key** (AI *video* generation especially — a few seconds of generated footage can be several dollars on its own). Best quality; use AI image/video/voice/music freely. When a run is likely to go well past $1, say the number before you spend it.
|
|
93
102
|
|
|
94
103
|
Then **ask whether to remember the choice**, and save it where it will actually be read next time:
|
|
95
104
|
|
|
@@ -1,3 +1,11 @@
|
|
|
1
|
+
## Free-plan API keys — valid everywhere, gated per route
|
|
2
|
+
|
|
3
|
+
A free (unpaid) account's `vidfarm-api-key` is a **real key**: it authenticates on every route, and `vidfarm login` / `whoami` work normally. Plan is enforced per route, not at auth:
|
|
4
|
+
|
|
5
|
+
- **Free on any plan** — `GET /api/v1/user/me` (identity), `/api/v1/user/me/provider-keys` (bring-your-own-key), `GET /api/v1/user/me/wallet` (reads $0.00), `GET /api/v1/user/me/tasks`, and the `/api/v1/user/me/directory*` file-tree routes (browse, search, folder, rename, move, copy, shares).
|
|
6
|
+
- **Paid routes answer `402 { type: "upgrade_required", upgrade_url: "/pricing", run_locally_url: "/help" }`** — never a 401. A `401` means the key is missing/invalid/revoked, so don't tell a free user to re-authenticate when they hit a 402.
|
|
7
|
+
- When you see a 402: say plainly that the action needs a paid plan, then offer the two real paths — upgrade at `/pricing`, or keep going for free locally (`vidfarm serve`, local render, local decompose, own-provider keys).
|
|
8
|
+
|
|
1
9
|
## Automate a template via REST
|
|
2
10
|
|
|
3
11
|
Templates now have the same job-backed REST pattern as primitives, so a script can run them repeatably without the editor UI:
|
|
@@ -172,7 +180,7 @@ The licensed harness also carries the **generative build workflow** guidance (ch
|
|
|
172
180
|
| `vidfarm login <email>` / `vidfarm verify <email> <code>` | `POST /api/v1/user/request-otp` · `verify-otp` | get an API key |
|
|
173
181
|
| `vidfarm whoami` | `GET /api/v1/user/me` | who am I (also prints the active cost mode) |
|
|
174
182
|
| `vidfarm cost-mode [minimize\|hybrid\|rich-ai]` | (local `~/.vidfarm/cost-mode.json`) | show/set the money-saving preference every billed command respects; no arg = show + explain the three; `--clear` forgets it |
|
|
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
|
|
183
|
+
| `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; readable on the free plan too (shows $0.00). Aliases: `spend`, `costs` |
|
|
176
184
|
| `vidfarm provider-keys` / `vidfarm add-provider-key <p> <secret>` | `GET`·`POST /api/v1/user/me/provider-keys` | manage AI keys |
|
|
177
185
|
| `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
186
|
| `vidfarm download <url> [dest]` | (streams any URL to disk) | download media from a **direct** media URL. Free — no plan, no job |
|
|
@@ -207,12 +215,24 @@ The licensed harness also carries the **generative build workflow** guidance (ch
|
|
|
207
215
|
|
|
208
216
|
## Cost mode — the devcli's money-saving guardrail
|
|
209
217
|
|
|
218
|
+
The three modes, quoted as **cost per finished video**:
|
|
219
|
+
|
|
220
|
+
| Mode | Per video | Posture |
|
|
221
|
+
|---|---|---|
|
|
222
|
+
| `minimize` | **$0 — explicitly free** | free local engines + free stock media only; billed ops refuse without `--yes` |
|
|
223
|
+
| `hybrid` *(default recommendation)* | **~$0.01–$1** | free where free, AI where it clearly wins; each billed op prints its cost |
|
|
224
|
+
| `rich-ai` | **$1+** | AI image/video/voice/music used freely; cost still printed |
|
|
225
|
+
|
|
226
|
+
**All of it bills to the user's own AI provider keys (BYOK)** — the keys saved with `vidfarm add-provider-key <provider> <key>` or at **Settings → Bring your own keys** (<https://vidfarm.cc/settings/developer>). The model providers charge those keys directly; Vidfarm wallet credits only come into play when the user deliberately runs on the platform key instead of their own. So `minimize` isn't "cheap", it's **zero**: nothing reaches a paid key at all.
|
|
227
|
+
|
|
210
228
|
`vidfarm cost-mode <minimize|hybrid|rich-ai>` records a single spend preference (in `~/.vidfarm/cost-mode.json`) that every **billed** command honors: `generate`, `music`, `decompose`, `inspiration-decompose`, `create`, `replicate`, `inpaint`, `create-overlay`, `cutout --generate` (only the generation step; a bare `cutout` on an existing graphic is free), and the cloud paths of `render --target cloud`, `tts --cloud`, `stt --cloud`, `remove-greenscreen` (non-`--local`). FREE local engines never gate (`render` local default, `tts --engine local`, `stt --engine whisper`, `remove-greenscreen --local`, `cutout` on a file/url, all the file-editing verbs). `vidfarm media search` (the free stock catalog — pixabay/openverse/iconify music, SFX, images, icons, stock video) is also free and never gates.
|
|
211
229
|
|
|
212
|
-
- **minimize** — a billed op is **refused** unless you add `--yes`; the error names the free local alternative (which now includes the matching `vidfarm media search` for music/SFX/image/video). Use this to guarantee no surprise AI spend. Before paying to generate music, sound effects, or images, try `vidfarm media search "<meaning>" --type <bgm|sfx|image|vector|icon|video>` first — free royalty-free assets instead of a billed `music`/`generate` call. **Check the keyless sources first — Openverse (CC/CC0 music, SFX, images) and iconify (icons) need no account at all**, so they always work in `minimize`. Photos/vectors/stock-video need a **free Pixabay key** that **may already be saved** — check `vidfarm provider-keys` (or web **Settings → Bring your own keys** / <https://vidfarm.cc/settings/developer>) before assuming a short result means "no key." If absent, save one once: `vidfarm add-provider-key pixabay <key>` (free key from <https://pixabay.com/api/docs/>), the Settings surface, or hand it to the desktop AI agent to run that command.
|
|
230
|
+
- **minimize ($0 videos)** — a billed op is **refused** unless you add `--yes`; the error names the free local alternative (which now includes the matching `vidfarm media search` for music/SFX/image/video). Use this to guarantee no surprise AI spend. Before paying to generate music, sound effects, or images, try `vidfarm media search "<meaning>" --type <bgm|sfx|image|vector|icon|video>` first — free royalty-free assets instead of a billed `music`/`generate` call. **Check the keyless sources first — Openverse (CC/CC0 music, SFX, images) and iconify (icons) need no account at all**, so they always work in `minimize`. Photos/vectors/stock-video need a **free Pixabay key** that **may already be saved** — check `vidfarm provider-keys` (or web **Settings → Bring your own keys** / <https://vidfarm.cc/settings/developer>) before assuming a short result means "no key." If absent, save one once: `vidfarm add-provider-key pixabay <key>` (free key from <https://pixabay.com/api/docs/>), the Settings surface, or hand it to the desktop AI agent to run that command.
|
|
213
231
|
- **minimize still gets CUSTOM images — via a free manual generator.** A refused `generate` is not the end of the road. Offer the user the manual loop (ask once, then make it the session default): **you write the prompt → they run it free in <https://meta.ai>, free-tier ChatGPT, or a free Hugging Face image Space (<https://huggingface.co/spaces>) → they hand the PNG back** via `vidfarm put-file ./sheet.png` or web **My Files**. Ask for **one sheet holding every graphic you need**, gridded on a **flat pure-green plate** (`#00FF00`), no text — one round trip instead of N, which saves the user's time and your tokens. Then split it locally for $0: `vidfarm mask ./sheet.png --crop x,y,w,h --flat "#00FF00" --out prop-a.png`, once per element (drop `--flat` and let local ONNX matting handle it if the tool ignored the green background). Full prompt template + loop: recipe `recipes/cutout-graphics-for-explainers.md` (“Free manual image-gen”).
|
|
214
|
-
- **hybrid** *(default recommendation)* — billed ops run but print a one-line cost notice each.
|
|
215
|
-
- **rich-ai** — billed ops run without gating; cost is still printed.
|
|
232
|
+
- **hybrid (~$0.01–$1 per video)** *(default recommendation)* — billed ops run but print a one-line cost notice each, charged to the user's BYOK key.
|
|
233
|
+
- **rich-ai ($1+ per video)** — billed ops run without gating; cost is still printed. AI *video* generation is the line item that pushes a video well past $1 — quote it before running.
|
|
234
|
+
|
|
235
|
+
**Narration defaults to the FREE local voice in minimize AND hybrid.** A bare `vidfarm tts "…"` runs the keyless local Kokoro-82M engine in both of those modes — you no longer have to remember `--engine local`. A run **opts out** of that default by asking for a premium voice (`--style`, `--provider`, `--model`, `--own-key`, or a non-Kokoro `--voice` like `alloy`/`Kore`/an ElevenLabs id), by passing `--cloud`/`--engine byok`, or by being in `rich-ai`. If the local engine isn't installed on the machine (it needs `pip install kokoro-onnx soundfile` + a ~340MB model on first use), the run **falls back** to the user's provider key / cloud instead of failing — it prints the reason on stderr so you can tell the user why the voice changed.
|
|
216
236
|
|
|
217
237
|
Precedence: `--cost-mode <m>` flag → `VIDFARM_COST_MODE` env → the saved `cost-mode` → default (hybrid, flagged as "not set"). When nothing is saved and a billed op runs, the CLI prints a "no preference set — ask the user" nudge instead of silently spending, so the default posture really is *ask before you spend*.
|
|
218
238
|
|
|
@@ -284,7 +284,7 @@ Vidfarm charges directly in USD from the caller's wallet. There are no credits.
|
|
|
284
284
|
|
|
285
285
|
- **Wallet top-up** — Stripe checkout via the Settings UI, backed by `POST /settings/wallet/funding-link` (browser session; returns `{ checkout_url, client_reference_id }`)
|
|
286
286
|
- **Balance & history** — shown on the Settings page; browser session also has `GET /u/:customerId/settings/wallet/events`.
|
|
287
|
-
- **Cost log (api-key / agent-friendly)** — `GET /api/v1/user/me/wallet` returns `{ summary, events, next_cursor }`. `summary` carries `balance_usd`, `total_charge_usd` (lifetime spend), `total_funds_added_usd`, and `total_event_count`; each `events[]` entry carries `amount_usd` (**negative = a charge/spend**), `occurred_at_ms`, `cost_center_slug`, `job_id`, and `tracer`. **To answer "how much did this video cost?"** add `?job=<jobId>` (or `?tracer=<tracer>`): the response then includes `filtered_charge_usd` (the summed spend for that render/video) and only the matching events.
|
|
287
|
+
- **Cost log (api-key / agent-friendly)** — `GET /api/v1/user/me/wallet` returns `{ summary, events, next_cursor }`. `summary` carries `balance_usd`, `total_charge_usd` (lifetime spend), `total_funds_added_usd`, and `total_event_count`; each `events[]` entry carries `amount_usd` (**negative = a charge/spend**), `occurred_at_ms`, `cost_center_slug`, `job_id`, and `tracer`. **To answer "how much did this video cost?"** add `?job=<jobId>` (or `?tracer=<tracer>`): the response then includes `filtered_charge_usd` (the summed spend for that render/video) and only the matching events. Readable on any plan — a free account simply shows a $0.00 balance with no events (paid work is what 402s, not reading your own wallet). This is the endpoint to use when a director asks what a video cost; devcli wraps it as `vidfarm wallet` / `vidfarm wallet --job <id>`.
|
|
288
288
|
- **Cost anchors** (see the Cost spectrum section above for how these combine per approach):
|
|
289
289
|
- Local render on a `vidfarm serve` box (in-process HyperFrames): **free**
|
|
290
290
|
- Cloud render (HyperFrames Lambda fan-out): typically $0.01 – $0.10 per MP4 depending on length/resolution
|
|
@@ -12,8 +12,9 @@ One engine and one rule. The engine: `scripts/audio.mjs` turns a neutral `audio_
|
|
|
12
12
|
Check which engines this run will use and say so in one line — then continue. There is nothing to sign in to and no reason to stop.
|
|
13
13
|
|
|
14
14
|
- Saved keys: `vidfarm provider-keys`, or env `OPENAI_API_KEY` / `GEMINI_API_KEY` / `OPENROUTER_API_KEY`.
|
|
15
|
-
- Key present → BYOK cloud speech: `vidfarm tts` (promptable voice via `--style`) and `vidfarm stt` (real word timestamps on an OpenAI whisper-1 key).
|
|
15
|
+
- Key present → BYOK cloud speech: `vidfarm tts` (promptable voice via `--style`) and `vidfarm stt` (real word timestamps on an OpenAI whisper-1 key). **But note the cost-mode default below — with a key saved, plain `vidfarm tts` still uses the FREE local voice in `minimize`/`hybrid` unless you ask for a premium one.**
|
|
16
16
|
- No key → keyless local engines: Kokoro-82M TTS (`npx hyperframes tts`) and whisper.cpp timings (`npx hyperframes transcribe`). They download model weights on first use and never need an account.
|
|
17
|
+
- **Cost mode drives the TTS engine.** The three modes quote a per-video budget — `minimize` = **$0**, `hybrid` = **~$0.01–$1**, `rich-ai` = **$1+**, all billed to the user's own AI provider keys (BYOK), never marked up by Vidfarm. `minimize` and `hybrid` (and the unset default) → free local Kokoro; `rich-ai`, or any premium ask (`--style`/`--provider`/`--model`/`--own-key`/non-Kokoro `--voice`/`--cloud`) → BYOK or cloud. A missing local engine falls back to BYOK/cloud automatically. Say which voice tier the narration is on the first time you generate it.
|
|
17
18
|
- Don't write keys into a per-repo `.env`. Details: `references/tts.md`.
|
|
18
19
|
|
|
19
20
|
## The audio engine
|
|
@@ -74,7 +75,7 @@ openrouter key; music always needs ElevenLabs (own key or platform).
|
|
|
74
75
|
| **Music** (bed, beat, jingle, song, score) | `vidfarm music "upbeat lo-fi beat" --length 30` · `POST /api/v1/primitives/music/generate` | ElevenLabs. `use_wallet_credits` default true (platform key + wallet); `--own-key` = your ElevenLabs key. `music_length_ms` ≤ 300000 (5 min). Place as its own `<audio>` layer ~0.1–0.2 under narration. |
|
|
75
76
|
| **Narration** (default) | `vidfarm tts "…" --cloud` · `POST /api/v1/primitives/audio/speech` | Default = ElevenLabs on the platform key (wallet-billed). Pick a voice with `--voice <voice_id>` (browse below). `--own-key` for your ElevenLabs/BYOK key. Local-first `vidfarm tts` (no `--cloud`) still runs on your env openai/gemini key. |
|
|
76
77
|
| **List voices** | `vidfarm voices` · `GET /api/v1/primitives/audio/voices` | ElevenLabs voice catalog (voice_id, name, labels, preview_url). Default = platform account; `--own-key` / `?use_wallet_credits=false` = the customer's ElevenLabs account. **Default a sensible voice AND tell the user they can pick from many.** |
|
|
77
|
-
| Narration, zero keys | `npx hyperframes tts "…" -v af_heart --json` | Kokoro-82M, local, WAV + duration in JSON |
|
|
78
|
+
| Narration, zero keys / cost-saving | `vidfarm tts "…" --out narration.wav` (free local by default in `minimize`/`hybrid`) · or `npx hyperframes tts "…" -v af_heart --json` | Kokoro-82M, local, WAV + duration in JSON. Fixed voice presets, no `--style`. |
|
|
78
79
|
| **Transcript + SRT** | `vidfarm stt <file\|url> --cloud` · `POST /api/v1/primitives/audio/transcribe` | Default = ElevenLabs Scribe (native diarization + real word timestamps), wallet-billed. `--own-key`/BYOK: gemini labels speakers, openai/whisper-1 gives real word timings. |
|
|
79
80
|
| Reword existing narration in the (approximate) original voice | `POST /api/v1/primitives/audio/regenerate-speech` | Listens, profiles the speaker (needs a Gemini key), rewords, regenerates with the closest preset voice + matched style. Approximation, never a clone. Details: `references/tts.md` |
|
|
80
81
|
| Word timings, zero keys | `npx hyperframes transcribe <audio> --json` | whisper.cpp; writes word-level `transcript.json` |
|
|
@@ -52,6 +52,7 @@ vidfarm voices --own-key # the customer's own ElevenLabs account voices
|
|
|
52
52
|
vidfarm tts "Welcome back to the channel" --style "calm, warm bedtime narrator" --voice alloy --out narration.mp3 --json
|
|
53
53
|
```
|
|
54
54
|
|
|
55
|
+
- **Cost-mode default: FREE local voice.** In cost mode `minimize` AND `hybrid` (including the unset default), a bare `vidfarm tts "…"` runs the keyless local Kokoro-82M engine — no `--engine local` needed. Opt out by asking for a premium voice (`--style`, `--provider`, `--model`, `--own-key`, or a non-Kokoro `--voice` such as `alloy`/`Kore`/an ElevenLabs id), by passing `--cloud` or `--engine byok`, or by being in `rich-ai`. If the local engine isn't installed the run **falls back** to the BYOK/cloud path below rather than failing, printing the reason on stderr.
|
|
55
56
|
- **Local-first**: runs directly on your env key (no cloud job, no wallet). `--cloud` is the explicit backup — an async platform job on your saved provider keys via `POST /api/v1/primitives/audio/speech` (poll the returned `job_id`).
|
|
56
57
|
- **Promptable voice**: `--style` (alias `--instructions`) directs tone/pacing/accent/emotion/persona — "excited sports announcer", "whispered documentary aside".
|
|
57
58
|
- **Voices** (provider inferred from the voice when `--provider` is omitted; wrong-family voices are rejected with a clear error):
|
package/SKILL.director.md
CHANGED
|
@@ -81,15 +81,24 @@ Do not use this skill to author new templates from scratch, deploy platform infr
|
|
|
81
81
|
|
|
82
82
|
## Cost mode — ask before you spend, then remember it
|
|
83
83
|
|
|
84
|
-
Vidfarm work can burn real AI credits on the user's wallet / provider keys. **Save them money by default.** Before the first billed step of a session, ask the user which spend posture they want, and
|
|
84
|
+
Vidfarm work can burn real AI credits on the user's wallet / provider keys. **Save them money by default.** Before the first billed step of a session, ask the user which spend posture they want, and quote the per-video cost in one plain line each:
|
|
85
85
|
|
|
86
|
-
|
|
86
|
+
| Mode | Cost per video | What it means |
|
|
87
|
+
|---|---|---|
|
|
88
|
+
| **minimize** | **$0 — explicitly free** | free local compute + free stock media only; billed AI is refused without `--yes` |
|
|
89
|
+
| **hybrid** *(recommended)* | **~$0.01–$1** | free where it's free; pay AI only where it clearly wins |
|
|
90
|
+
| **rich-ai** | **$1+** | best quality; AI image/video/voice/music used freely |
|
|
91
|
+
|
|
92
|
+
**Every dollar above is billed to the user's OWN AI provider keys (BYOK)** — the keys they save via `vidfarm add-provider-key <provider> <key>` or **Settings → Bring your own keys** (<https://vidfarm.cc/settings/developer>). Vidfarm isn't marking it up; the model providers charge it directly. (Vidfarm wallet credits are only spent when the user deliberately runs on the platform key instead of their own.) Say this out loud the first time you ask — it's why `minimize` really is $0: with no key spend, nothing can be charged.
|
|
93
|
+
|
|
94
|
+
- **minimize** — **$0 videos.** Stay on FREE local compute wherever possible (local render, local TTS — `vidfarm tts` already defaults to the free local Kokoro voice in this mode — `stt --engine whisper`, `remove-greenscreen --local`, reused raw clips + HTML hyperframes). For assets, reach for the **free stock catalog** before paying to generate anything — `vidfarm media search "<meaning>" --type <bgm|sfx|image|vector|icon|video>` pulls royalty-free, commercial-safe music/sound-effects/images/icons/stock-video from pixabay/openverse/iconify at $0 instead of billing AI music/image generation (see the `vidfarm-media` skill). No surprise AI spend.
|
|
87
95
|
- **Check the keyless sources first — Openverse and iconify.** Openverse (CC/CC0 **music, SFX, and images**) and iconify (**icons**) need **no account or key at all**, so they always work in `minimize` mode. Prefer them for BGM, sound effects, icons, and CC imagery before anything else.
|
|
88
96
|
- **Pixabay key** unlocks the photos/vectors/stock-video slots (music/SFX/icons/CC images are keyless). It's a **free** stock-media key, not an AI key. Don't assume it's missing when a search comes up short — it **may already be saved**: check `vidfarm provider-keys` (or the web app's **Settings → Bring your own keys** / <https://vidfarm.cc/settings/developer>). If it isn't, the user grabs a free one at <https://pixabay.com/api/docs/> and saves it once — `vidfarm add-provider-key pixabay <key>`, the Settings surface, or by handing the key to their desktop AI agent to run that command. After it's saved, cost-mode `minimize` sourcing works end-to-end at $0.
|
|
89
97
|
- **You can still get CUSTOM art in `minimize` — hand the prompt to the user and let a free image generator do it.** Stock and `mask` only cover art that already exists somewhere; when the video genuinely needs a bespoke graphic, **don't conclude "we can't" and don't quietly bill `generate`**. Write the prompt and ask the user to paste it into a **free** image generator — <https://meta.ai>, free-tier ChatGPT, or a free Hugging Face image Space (<https://huggingface.co/spaces>) — then hand the PNG back with `vidfarm put-file` (or drag it into **My Files** in the web app). $0, zero wallet spend. Full loop + the prompt template: **“Free manual image-gen”** below.
|
|
90
|
-
- **hybrid** *(recommend this)* —
|
|
91
|
-
- **
|
|
92
|
-
- **
|
|
98
|
+
- **hybrid** *(recommend this)* — **~$0.01–$1 per video, on their BYOK key.** Free where it's free; pay for AI only where it clearly wins (a hero shot, a voice you can't fake locally). A mostly-hyperframes video with one generated image lands near the low end; a few AI images plus premium narration approaches the high end.
|
|
99
|
+
- **Narration starts free here too.** In BOTH `minimize` and `hybrid`, a bare `vidfarm tts "…"` runs the keyless local Kokoro-82M voice — no flag needed. Ask for a premium voice explicitly (`--style`, `--voice <provider preset>`, `--provider`, `--own-key`, or `--cloud`) when the piece needs one, and say so out loud so the user knows what they're paying for. `rich-ai` skips the free default entirely. If the local engine isn't installed on their machine, the run falls back to their provider key / cloud automatically and prints why.
|
|
100
|
+
- **Free-TTS draft, premium-TTS final — now the default shape, so SAY it out loud.** Narration gets rewritten several times before the script locks, and paying for premium ElevenLabs TTS on every draft wastes money re-voicing lines that will change. The CLI defaults to the free local voice in `hybrid`, so drafts are free automatically — but a robotic draft voice reads as "broken" to a user who wasn't told. **Tell them the first time you generate narration**: the draft voice is the free local one, and you'll re-voice the approved final script in premium TTS (`--cloud` ElevenLabs, or `--style`/`--voice` on their own key) right before the last render. If they'd rather hear the real voice the whole way, pass the premium flags from the start — and if the piece is voice-led (a testimonial, a brand spot where the voice IS the product), offer premium up front instead of drafting free.
|
|
101
|
+
- **rich-ai** — **$1+ per video, on their BYOK key** (AI *video* generation especially — a few seconds of generated footage can be several dollars on its own). Best quality; use AI image/video/voice/music freely. When a run is likely to go well past $1, say the number before you spend it.
|
|
93
102
|
|
|
94
103
|
Then **ask whether to remember the choice**, and save it where it will actually be read next time:
|
|
95
104
|
|
|
@@ -552,7 +561,7 @@ Vidfarm charges directly in USD from the caller's wallet. There are no credits.
|
|
|
552
561
|
|
|
553
562
|
- **Wallet top-up** — Stripe checkout via the Settings UI, backed by `POST /settings/wallet/funding-link` (browser session; returns `{ checkout_url, client_reference_id }`)
|
|
554
563
|
- **Balance & history** — shown on the Settings page; browser session also has `GET /u/:customerId/settings/wallet/events`.
|
|
555
|
-
- **Cost log (api-key / agent-friendly)** — `GET /api/v1/user/me/wallet` returns `{ summary, events, next_cursor }`. `summary` carries `balance_usd`, `total_charge_usd` (lifetime spend), `total_funds_added_usd`, and `total_event_count`; each `events[]` entry carries `amount_usd` (**negative = a charge/spend**), `occurred_at_ms`, `cost_center_slug`, `job_id`, and `tracer`. **To answer "how much did this video cost?"** add `?job=<jobId>` (or `?tracer=<tracer>`): the response then includes `filtered_charge_usd` (the summed spend for that render/video) and only the matching events.
|
|
564
|
+
- **Cost log (api-key / agent-friendly)** — `GET /api/v1/user/me/wallet` returns `{ summary, events, next_cursor }`. `summary` carries `balance_usd`, `total_charge_usd` (lifetime spend), `total_funds_added_usd`, and `total_event_count`; each `events[]` entry carries `amount_usd` (**negative = a charge/spend**), `occurred_at_ms`, `cost_center_slug`, `job_id`, and `tracer`. **To answer "how much did this video cost?"** add `?job=<jobId>` (or `?tracer=<tracer>`): the response then includes `filtered_charge_usd` (the summed spend for that render/video) and only the matching events. Readable on any plan — a free account simply shows a $0.00 balance with no events (paid work is what 402s, not reading your own wallet). This is the endpoint to use when a director asks what a video cost; devcli wraps it as `vidfarm wallet` / `vidfarm wallet --job <id>`.
|
|
556
565
|
- **Cost anchors** (see the Cost spectrum section above for how these combine per approach):
|
|
557
566
|
- Local render on a `vidfarm serve` box (in-process HyperFrames): **free**
|
|
558
567
|
- Cloud render (HyperFrames Lambda fan-out): typically $0.01 – $0.10 per MP4 depending on length/resolution
|
|
@@ -1175,6 +1184,14 @@ The web copilot exposes the same three as `browse_files action=rename|move|copy`
|
|
|
1175
1184
|
|
|
1176
1185
|
In the editor web copilot the same filesystem is exposed via the **`browse_files` tool** (`action=list` / `action=search` / `action=read` / `action=write` / `action=annotate` / `action=move` / `action=copy` / `action=rename`), so the copilot follows the identical reasoning: search or list to find the right offer's folder, then read an asset — or `write` a text doc (About.md, awareness-levels.md, …) or import a media URL (`source_url`) into that folder, annotating anything worth finding again. `action=rename`/`move`/`copy` reorganize the tree (see the three routes above) — use them to keep character folders and asset names tidy. `browse_files list` defaults to `path='/'` when `path` is omitted, so the `/raws` (hunted raws) and `/temp` (scratch) roots surface alongside the My Files folders instead of being hidden; a `/raws` listing also accepts a `content_type` filter (exact shot-kind — `talking_head`, `b_roll`, `product_shot`, `screen_recording`, …), and every listing paginates via `offset` / `limit`. The devcli equivalents are `vidfarm files [--search]` / `get-file` / `put-file [--notes]` / `annotate-file` / `directory rename|move|copy`.
|
|
1177
1186
|
|
|
1187
|
+
## Free-plan API keys — valid everywhere, gated per route
|
|
1188
|
+
|
|
1189
|
+
A free (unpaid) account's `vidfarm-api-key` is a **real key**: it authenticates on every route, and `vidfarm login` / `whoami` work normally. Plan is enforced per route, not at auth:
|
|
1190
|
+
|
|
1191
|
+
- **Free on any plan** — `GET /api/v1/user/me` (identity), `/api/v1/user/me/provider-keys` (bring-your-own-key), `GET /api/v1/user/me/wallet` (reads $0.00), `GET /api/v1/user/me/tasks`, and the `/api/v1/user/me/directory*` file-tree routes (browse, search, folder, rename, move, copy, shares).
|
|
1192
|
+
- **Paid routes answer `402 { type: "upgrade_required", upgrade_url: "/pricing", run_locally_url: "/help" }`** — never a 401. A `401` means the key is missing/invalid/revoked, so don't tell a free user to re-authenticate when they hit a 402.
|
|
1193
|
+
- When you see a 402: say plainly that the action needs a paid plan, then offer the two real paths — upgrade at `/pricing`, or keep going for free locally (`vidfarm serve`, local render, local decompose, own-provider keys).
|
|
1194
|
+
|
|
1178
1195
|
## Automate a template via REST
|
|
1179
1196
|
|
|
1180
1197
|
Templates now have the same job-backed REST pattern as primitives, so a script can run them repeatably without the editor UI:
|
|
@@ -1349,7 +1366,7 @@ The licensed harness also carries the **generative build workflow** guidance (ch
|
|
|
1349
1366
|
| `vidfarm login <email>` / `vidfarm verify <email> <code>` | `POST /api/v1/user/request-otp` · `verify-otp` | get an API key |
|
|
1350
1367
|
| `vidfarm whoami` | `GET /api/v1/user/me` | who am I (also prints the active cost mode) |
|
|
1351
1368
|
| `vidfarm cost-mode [minimize\|hybrid\|rich-ai]` | (local `~/.vidfarm/cost-mode.json`) | show/set the money-saving preference every billed command respects; no arg = show + explain the three; `--clear` forgets it |
|
|
1352
|
-
| `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
|
|
1369
|
+
| `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; readable on the free plan too (shows $0.00). Aliases: `spend`, `costs` |
|
|
1353
1370
|
| `vidfarm provider-keys` / `vidfarm add-provider-key <p> <secret>` | `GET`·`POST /api/v1/user/me/provider-keys` | manage AI keys |
|
|
1354
1371
|
| `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 |
|
|
1355
1372
|
| `vidfarm download <url> [dest]` | (streams any URL to disk) | download media from a **direct** media URL. Free — no plan, no job |
|
|
@@ -1384,12 +1401,24 @@ The licensed harness also carries the **generative build workflow** guidance (ch
|
|
|
1384
1401
|
|
|
1385
1402
|
## Cost mode — the devcli's money-saving guardrail
|
|
1386
1403
|
|
|
1404
|
+
The three modes, quoted as **cost per finished video**:
|
|
1405
|
+
|
|
1406
|
+
| Mode | Per video | Posture |
|
|
1407
|
+
|---|---|---|
|
|
1408
|
+
| `minimize` | **$0 — explicitly free** | free local engines + free stock media only; billed ops refuse without `--yes` |
|
|
1409
|
+
| `hybrid` *(default recommendation)* | **~$0.01–$1** | free where free, AI where it clearly wins; each billed op prints its cost |
|
|
1410
|
+
| `rich-ai` | **$1+** | AI image/video/voice/music used freely; cost still printed |
|
|
1411
|
+
|
|
1412
|
+
**All of it bills to the user's own AI provider keys (BYOK)** — the keys saved with `vidfarm add-provider-key <provider> <key>` or at **Settings → Bring your own keys** (<https://vidfarm.cc/settings/developer>). The model providers charge those keys directly; Vidfarm wallet credits only come into play when the user deliberately runs on the platform key instead of their own. So `minimize` isn't "cheap", it's **zero**: nothing reaches a paid key at all.
|
|
1413
|
+
|
|
1387
1414
|
`vidfarm cost-mode <minimize|hybrid|rich-ai>` records a single spend preference (in `~/.vidfarm/cost-mode.json`) that every **billed** command honors: `generate`, `music`, `decompose`, `inspiration-decompose`, `create`, `replicate`, `inpaint`, `create-overlay`, `cutout --generate` (only the generation step; a bare `cutout` on an existing graphic is free), and the cloud paths of `render --target cloud`, `tts --cloud`, `stt --cloud`, `remove-greenscreen` (non-`--local`). FREE local engines never gate (`render` local default, `tts --engine local`, `stt --engine whisper`, `remove-greenscreen --local`, `cutout` on a file/url, all the file-editing verbs). `vidfarm media search` (the free stock catalog — pixabay/openverse/iconify music, SFX, images, icons, stock video) is also free and never gates.
|
|
1388
1415
|
|
|
1389
|
-
- **minimize** — a billed op is **refused** unless you add `--yes`; the error names the free local alternative (which now includes the matching `vidfarm media search` for music/SFX/image/video). Use this to guarantee no surprise AI spend. Before paying to generate music, sound effects, or images, try `vidfarm media search "<meaning>" --type <bgm|sfx|image|vector|icon|video>` first — free royalty-free assets instead of a billed `music`/`generate` call. **Check the keyless sources first — Openverse (CC/CC0 music, SFX, images) and iconify (icons) need no account at all**, so they always work in `minimize`. Photos/vectors/stock-video need a **free Pixabay key** that **may already be saved** — check `vidfarm provider-keys` (or web **Settings → Bring your own keys** / <https://vidfarm.cc/settings/developer>) before assuming a short result means "no key." If absent, save one once: `vidfarm add-provider-key pixabay <key>` (free key from <https://pixabay.com/api/docs/>), the Settings surface, or hand it to the desktop AI agent to run that command.
|
|
1416
|
+
- **minimize ($0 videos)** — a billed op is **refused** unless you add `--yes`; the error names the free local alternative (which now includes the matching `vidfarm media search` for music/SFX/image/video). Use this to guarantee no surprise AI spend. Before paying to generate music, sound effects, or images, try `vidfarm media search "<meaning>" --type <bgm|sfx|image|vector|icon|video>` first — free royalty-free assets instead of a billed `music`/`generate` call. **Check the keyless sources first — Openverse (CC/CC0 music, SFX, images) and iconify (icons) need no account at all**, so they always work in `minimize`. Photos/vectors/stock-video need a **free Pixabay key** that **may already be saved** — check `vidfarm provider-keys` (or web **Settings → Bring your own keys** / <https://vidfarm.cc/settings/developer>) before assuming a short result means "no key." If absent, save one once: `vidfarm add-provider-key pixabay <key>` (free key from <https://pixabay.com/api/docs/>), the Settings surface, or hand it to the desktop AI agent to run that command.
|
|
1390
1417
|
- **minimize still gets CUSTOM images — via a free manual generator.** A refused `generate` is not the end of the road. Offer the user the manual loop (ask once, then make it the session default): **you write the prompt → they run it free in <https://meta.ai>, free-tier ChatGPT, or a free Hugging Face image Space (<https://huggingface.co/spaces>) → they hand the PNG back** via `vidfarm put-file ./sheet.png` or web **My Files**. Ask for **one sheet holding every graphic you need**, gridded on a **flat pure-green plate** (`#00FF00`), no text — one round trip instead of N, which saves the user's time and your tokens. Then split it locally for $0: `vidfarm mask ./sheet.png --crop x,y,w,h --flat "#00FF00" --out prop-a.png`, once per element (drop `--flat` and let local ONNX matting handle it if the tool ignored the green background). Full prompt template + loop: recipe `recipes/cutout-graphics-for-explainers.md` (“Free manual image-gen”).
|
|
1391
|
-
- **hybrid** *(default recommendation)* — billed ops run but print a one-line cost notice each.
|
|
1392
|
-
- **rich-ai** — billed ops run without gating; cost is still printed.
|
|
1418
|
+
- **hybrid (~$0.01–$1 per video)** *(default recommendation)* — billed ops run but print a one-line cost notice each, charged to the user's BYOK key.
|
|
1419
|
+
- **rich-ai ($1+ per video)** — billed ops run without gating; cost is still printed. AI *video* generation is the line item that pushes a video well past $1 — quote it before running.
|
|
1420
|
+
|
|
1421
|
+
**Narration defaults to the FREE local voice in minimize AND hybrid.** A bare `vidfarm tts "…"` runs the keyless local Kokoro-82M engine in both of those modes — you no longer have to remember `--engine local`. A run **opts out** of that default by asking for a premium voice (`--style`, `--provider`, `--model`, `--own-key`, or a non-Kokoro `--voice` like `alloy`/`Kore`/an ElevenLabs id), by passing `--cloud`/`--engine byok`, or by being in `rich-ai`. If the local engine isn't installed on the machine (it needs `pip install kokoro-onnx soundfile` + a ~340MB model on first use), the run **falls back** to the user's provider key / cloud instead of failing — it prints the reason on stderr so you can tell the user why the voice changed.
|
|
1393
1422
|
|
|
1394
1423
|
Precedence: `--cost-mode <m>` flag → `VIDFARM_COST_MODE` env → the saved `cost-mode` → default (hybrid, flagged as "not set"). When nothing is saved and a billed op runs, the CLI prints a "no preference set — ask the user" nudge instead of silently spending, so the default posture really is *ask before you spend*.
|
|
1395
1424
|
|
package/SKILL.md
CHANGED
|
@@ -26,7 +26,15 @@ Default to the cheapest approach that works, and surface cost before any expensi
|
|
|
26
26
|
|
|
27
27
|
## Save the user money — ask their cost mode first
|
|
28
28
|
|
|
29
|
-
Before spending any AI credits, ask which posture they want and
|
|
29
|
+
Before spending any AI credits, ask which posture they want and **quote the per-video cost**:
|
|
30
|
+
|
|
31
|
+
| Mode | Cost per video | What it means |
|
|
32
|
+
|---|---|---|
|
|
33
|
+
| **minimize** | **$0 — explicitly free** | free local compute + free stock media; no AI spend at all |
|
|
34
|
+
| **hybrid** *(recommended)* | **~$0.01–$1** | free where free, pay AI only where it clearly wins |
|
|
35
|
+
| **rich-ai** *(stored as `pure-ai`)* | **$1+** | best quality; AI image/video/voice/music freely |
|
|
36
|
+
|
|
37
|
+
**Those charges go to the user's own AI provider keys (BYOK)** — saved via `vidfarm add-provider-key <provider> <key>` or **Settings → Bring your own keys** (<https://vidfarm.cc/settings/developer>). The providers bill those keys directly; Vidfarm wallet credits only apply when the user deliberately runs on the platform key. That's why `minimize` is genuinely $0, not just cheap. With the devcli, persist it via `vidfarm cost-mode <choice>` (billed commands then respect it; `minimize` refuses billed ops without `--yes`). **Narration follows the mode automatically**: in `minimize` and `hybrid`, plain `vidfarm tts "…"` uses the free local Kokoro voice — ask for a premium voice explicitly (`--style`/`--voice`/`--provider`/`--cloud`) when the piece needs one, and tell the user which tier they're hearing. Then ask if they want it remembered across sessions — save to the agent's own memory (Claude Code → `CLAUDE.md`; Codex/OpenCode/others → `AGENTS.md`; or a note they name), asking first. In the **web app UI** there's no memory file to write, so just ask each time unless they state a standing preference. Full guidance is in the director skill.
|
|
30
38
|
|
|
31
39
|
**Even in `minimize`, you can still get custom images — for free, manually.** When stock and masking existing art won't cover a bespoke graphic, don't say "not possible" and don't quietly spend: ask the user once whether to **default to free manual image generation**, then write the prompt and have them run it in <https://meta.ai>, free-tier ChatGPT, or a free Hugging Face image Space, and hand the PNG back (`vidfarm put-file`, or drag into My Files). Ask for **one image containing every graphic you need**, gridded on a **flat pure-green background** (`#00FF00`) with no text — one round trip instead of many, which is the token-efficient move. Then split it locally at $0: `vidfarm mask ./sheet.png --crop x,y,w,h --flat "#00FF00"` once per element gives you snug transparent stickers. Prompt template + full loop in the director skill.
|
|
32
40
|
|
|
@@ -64,6 +72,7 @@ This page does not contain the workflows. Pull the full director skill and load
|
|
|
64
72
|
- `onboarding` — "help me figure out what to make" interview
|
|
65
73
|
- `primitives` — TTS, STT, music, overlays, background removal, product placement
|
|
66
74
|
- `rest-api` — complete REST map (load only for direct HTTP integration)
|
|
75
|
+
- **Updating Vidfarm:** `https://vidfarm.cc/update.md` — **fetch this file whenever the user asks to update/upgrade Vidfarm**, says their skill or devcli is out of date, or hits a command/route that "used to work". It's the clean uninstall→reinstall runbook for BOTH halves (the `vidfarm` skill pack and `@officexapp/vidfarm-devcli`), and it carries the current breaking-change notes — read it live rather than improvising an `npm i -g`, because the notes are exactly what a blind reinstall misses.
|
|
67
76
|
- **Skill pack index (what else is installable):** `https://vidfarm.cc/skill-pack/index.json`
|
|
68
77
|
- **Platform architecture (how it all fits together):** `https://vidfarm.cc/skill/vidfarm-platform`
|
|
69
78
|
|
|
@@ -78,6 +87,7 @@ For composition *authoring* craft (motion, keyframes, scene design), Vidfarm shi
|
|
|
78
87
|
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`.
|
|
79
88
|
5. "Script / batch / render loop" → `references/automation-and-local-dev.md`
|
|
80
89
|
6. "I need TTS / music / captions / background removal" → `references/primitives.md`
|
|
90
|
+
7. **"Update / upgrade vidfarm"** (or anything that smells like a stale install — a missing command, a 404 on a documented route, a version mismatch) → fetch <https://vidfarm.cc/update.md> and follow it. Update the skill pack and the devcli **together**; updating one alone is the usual cause of "the skill says to do X but it fails."
|
|
81
91
|
|
|
82
92
|
## Non-negotiables
|
|
83
93
|
|
package/clipper.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: clipper
|
|
3
|
-
description: Run an agentic clipper loop — earn side income by having your own AI agent work a DollarPlatoon gig (vending machine) unattended on your own computer. Use this whenever someone wants to set up, run, debug, or get paid from a clipper loop, mentions a DollarPlatoon gig invite link or
|
|
3
|
+
description: Run an agentic clipper loop — earn side income by having your own AI agent work a DollarPlatoon gig (vending machine) unattended on your own computer. Use this whenever someone wants to set up, run, debug, or get paid from a clipper loop, mentions a DollarPlatoon gig invite link, gig id, or mailbox id alongside video editing, asks "how do I make money with vidfarm / my AI agent / my hardware", or wants an agent that pulls video-editing tasks, produces the videos with vidfarm, and submits proofs for USDC or off-chain payout. Served at vidfarm.cc/clipper.md. Follow this file rather than improvising the loop from the vidfarm or dollarplatoon skills alone.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# /clipper — the agentic clipper loop
|
|
@@ -30,7 +30,7 @@ Read this whole file before doing anything. Then follow the phases in order.
|
|
|
30
30
|
|---|---|
|
|
31
31
|
| `VIDFARM_API_KEY` | `.env`, or `vidfarm login <email>` (email code). Get one at <https://vidfarm.cc> |
|
|
32
32
|
| `DOLLARPLATOON_API_KEY` | `.env`, or <https://dollarplatoon.com/client/settings>. Alias: `DOLLAR_PLATOON_API_KEY` |
|
|
33
|
-
| A gig |
|
|
33
|
+
| A gig | Any one of three: an invite URL like `https://dollarplatoon.com/gig/GIG_01HX.../join?invite=a1b2c3d4e5f6`, a bare gig id (`GIG_01HX…`), or — if they already joined — their **mailbox id** for that gig |
|
|
34
34
|
| The Vidfarm skill | Fetch <https://vidfarm.cc/skill.md> — it routes you to the full director skill |
|
|
35
35
|
| The DollarPlatoon skill | Fetch <https://dollarplatoon.com/skill.md> — the full REST reference |
|
|
36
36
|
|
|
@@ -52,7 +52,9 @@ what they agreed to.
|
|
|
52
52
|
|
|
53
53
|
Ask these, in one message, with the defaults stated:
|
|
54
54
|
|
|
55
|
-
1. **Which gig?** "Paste the DollarPlatoon gig invite link
|
|
55
|
+
1. **Which gig?** "Paste the DollarPlatoon gig invite link — or, if you've already joined, just the
|
|
56
|
+
gig id or your mailbox id for it." — required, no default. If they have none of the three but do
|
|
57
|
+
have an API key, run `GET /mailboxes/mine` and show them the gigs they're already in to pick from.
|
|
56
58
|
2. **Cost mode — default `minimize`.** Cheapest: free local rendering on their machine, no surprise
|
|
57
59
|
AI spend. `hybrid` pays for AI only where it clearly wins. `rich-ai` is best quality and most
|
|
58
60
|
expensive. Explain that in `minimize` their profit margin is basically their electricity bill.
|
|
@@ -76,9 +78,30 @@ later without you.
|
|
|
76
78
|
|
|
77
79
|
## Phase 2 — Resolve and inspect the gig
|
|
78
80
|
|
|
79
|
-
**
|
|
80
|
-
|
|
81
|
-
|
|
81
|
+
**First, work out what they actually gave you.** Three inputs are valid, and they need different
|
|
82
|
+
handling — figure out which one it is before you call anything:
|
|
83
|
+
|
|
84
|
+
- **An invite URL** — `https://dollarplatoon.com/gig/<GIG_ID>/join?invite=<TOKEN>` gives you both
|
|
85
|
+
the gig id and the invite token. This is the only form that can join a fresh `invite`-only gig.
|
|
86
|
+
- **A bare gig id** (`GIG_01HX…`) — no token. If they've already joined, that's fine (see below). If
|
|
87
|
+
they haven't and the gig is `join_policy: "invite"`, the join will 403 — ask for the full link.
|
|
88
|
+
- **A mailbox id** — their own mailbox on a gig they already joined. This is enough on its own: the
|
|
89
|
+
mailbox *is* the membership, so there is nothing to join and no invite token needed.
|
|
90
|
+
|
|
91
|
+
**Resolve a mailbox id (or check an existing membership) with `GET /mailboxes/mine`:**
|
|
92
|
+
|
|
93
|
+
```bash
|
|
94
|
+
curl -s -H "x-api-key: $DOLLARPLATOON_API_KEY" https://dollarplatoon.com/api/mailboxes/mine
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
Each entry carries `id`, `gig_id`, `gig_title`, and `status`. Match on `id` for a mailbox id, or on
|
|
98
|
+
`gig_id` for a bare gig id. This is also the cheapest way to answer "which gigs am I already on?" —
|
|
99
|
+
run it whenever the user is vague about which gig they mean, and let them pick from the list.
|
|
100
|
+
|
|
101
|
+
**Always run this before joining, even when you have an invite URL.** If a mailbox for that gig
|
|
102
|
+
already exists, **skip Phase 2's join entirely and reuse the existing `mailbox.id`** — joining twice
|
|
103
|
+
is a duplicate, not a fresh start. And check `status`: an `inactive` mailbox means the client
|
|
104
|
+
disabled them, so stop and tell the user rather than polling into a wall.
|
|
82
105
|
|
|
83
106
|
**Look at the gig before joining:**
|
|
84
107
|
|
|
@@ -101,7 +124,7 @@ Read and report back to the user, in plain language:
|
|
|
101
124
|
machine? If the gig wants 4K motion-graphics work and cost mode is `minimize`, say so now. A gig
|
|
102
125
|
you cannot deliver is worth zero and costs reputation — declining is a real, correct answer.
|
|
103
126
|
|
|
104
|
-
**Join:**
|
|
127
|
+
**Join — only if `GET /mailboxes/mine` showed no mailbox for this gig:**
|
|
105
128
|
|
|
106
129
|
```bash
|
|
107
130
|
curl -s -X POST -H "x-api-key: $DOLLARPLATOON_API_KEY" -H "content-type: application/json" \
|
|
@@ -155,6 +178,7 @@ async function dp(path, { method = "GET", body } = {}) {
|
|
|
155
178
|
return json;
|
|
156
179
|
}
|
|
157
180
|
|
|
181
|
+
export const myMailboxes = () => dp(`/mailboxes/mine`); // resolve a mailbox id → gig_id, or list gigs already joined
|
|
158
182
|
export const getGig = (gigId) => dp(`/gigs/${gigId}`);
|
|
159
183
|
export const pollQueue = (gigId, count = 1) => dp(`/gigs/${gigId}/queue/poll`, { method: "POST", body: { count } });
|
|
160
184
|
export const declineTask = (gigId, msgId) => dp(`/gigs/${gigId}/queue/${msgId}/decline`, { method: "POST" });
|
|
@@ -378,7 +402,9 @@ These exist because the loop touches real customers, real reputation, and real m
|
|
|
378
402
|
| Symptom | Likely cause | Fix |
|
|
379
403
|
|---|---|---|
|
|
380
404
|
| Poll returns no tasks | Gig isn't `distribution: "queue"` | Check `gig.distribution`, use Phase 4's matching path |
|
|
381
|
-
| `403` on join | Missing or revoked invite token |
|
|
405
|
+
| `403` on join | Missing or revoked invite token | Check `GET /mailboxes/mine` first — they may already be in; otherwise ask for the full invite URL again |
|
|
406
|
+
| User only has a mailbox id | Already joined; no invite needed | `GET /mailboxes/mine`, match on `id`, use its `gig_id` and skip the join |
|
|
407
|
+
| Mailbox `status: "inactive"` | Client disabled them | Stop the loop; they have to talk to the client |
|
|
382
408
|
| Join returns `pending_approval` | Gig has `requires_approval` | Wait for the client; don't poll |
|
|
383
409
|
| `429` on poll or proof | Worker rate limit | Sleep until `rate_limit.retry_at` |
|
|
384
410
|
| `409` on proof | Duplicate `task_identifier` | You already submitted; check `GET /gigs/:id/proofs` |
|
package/dist/src/cli.js
CHANGED
|
@@ -79,7 +79,9 @@ Account (persisted login — points the CLI + \`serve\` at cloud prod):
|
|
|
79
79
|
logout Clear the persisted credential
|
|
80
80
|
whoami Show the logged-in account, host, plan, and cost mode
|
|
81
81
|
cost-mode [mode] Show or set the money-saving preference all billed
|
|
82
|
-
commands respect
|
|
82
|
+
commands respect, quoted per finished video:
|
|
83
|
+
minimize = $0 · hybrid = ~$0.01-$1 · rich-ai = $1+.
|
|
84
|
+
Spend is billed to YOUR AI provider keys (BYOK).
|
|
83
85
|
No arg = show current + explain the three simply.
|
|
84
86
|
--clear forgets it · --note "<why>" annotates the save.
|
|
85
87
|
Override per-run with --cost-mode <m> / VIDFARM_COST_MODE;
|
|
@@ -407,8 +409,9 @@ Raws (the third library — mine long-form video into a reusable raws store):
|
|
|
407
409
|
save only that exact clipped range.
|
|
408
410
|
|
|
409
411
|
Speech (TTS/STT) — LOCAL-FIRST on your own AI key; --cloud is the explicit backup:
|
|
410
|
-
tts "<text>" Text → narration audio file.
|
|
411
|
-
The VOICE STYLE is promptable via --style.
|
|
412
|
+
tts "<text>" Text → narration audio file. FREE local Kokoro by default in
|
|
413
|
+
The VOICE STYLE is promptable via --style. cost mode minimize/hybrid; else
|
|
414
|
+
your OPENAI/GEMINI/OPENROUTER key
|
|
412
415
|
--style "<direction>" Voice-style prompt: tone/pacing/accent/emotion/persona
|
|
413
416
|
(e.g. "calm, warm bedtime narrator", "excited sports announcer")
|
|
414
417
|
--voice <name> Provider voice preset (openai: alloy/ash/coral…; gemini: Kore/Puck…).
|
|
@@ -417,8 +420,13 @@ Speech (TTS/STT) — LOCAL-FIRST on your own AI key; --cloud is the explicit bac
|
|
|
417
420
|
--out <file> Output audio path (default tts-<id>.mp3|wav)
|
|
418
421
|
--provider <p> --model <m> --format mp3|wav
|
|
419
422
|
--engine local KEYLESS local Kokoro-82M engine (free, no account) — fixed voice
|
|
420
|
-
presets (af_heart, am_adam, bf_emma, …), no --style.
|
|
421
|
-
|
|
423
|
+
presets (af_heart, am_adam, bf_emma, …), no --style. This is the
|
|
424
|
+
DEFAULT in cost mode minimize/hybrid; it also auto-runs when NO
|
|
425
|
+
provider key is found. Asking for a premium voice (--style,
|
|
426
|
+
--provider, --model, --own-key, or a non-Kokoro --voice) opts out,
|
|
427
|
+
as does cost mode rich-ai. If the local engine is unavailable the
|
|
428
|
+
run falls back to your provider key / cloud instead of failing.
|
|
429
|
+
--engine byok Force your own provider key even in minimize/hybrid.
|
|
422
430
|
--cloud BACKUP: platform job → POST /api/v1/primitives/audio/speech (+ poll).
|
|
423
431
|
Defaults to ElevenLabs on the platform key (wallet-billed); --voice
|
|
424
432
|
takes an ElevenLabs voice_id (see: vidfarm voices).
|
|
@@ -680,11 +688,16 @@ Cost spectrum (default to the cheapest approach that works; see SKILL.director.m
|
|
|
680
688
|
$10+ Heavy AI generation (many/long AI clips, custom characters).
|
|
681
689
|
Notes: image gen is cheap (use freely); AI VIDEO gen is expensive (ask the user
|
|
682
690
|
first). Decompose is a one-time ~$0.10 — or skip it by forking a decomposed template.
|
|
691
|
+
Per-video budget by mode: minimize = $0 (explicitly free), hybrid = ~$0.01-$1,
|
|
692
|
+
rich-ai = $1+. All of it is billed to YOUR OWN AI provider keys (BYOK) — add them
|
|
693
|
+
with 'vidfarm add-provider-key <provider> <key>' or at vidfarm.cc/settings/developer.
|
|
683
694
|
Set 'vidfarm cost-mode <minimize|hybrid|rich-ai>' once and every billed command
|
|
684
695
|
(generate, music, decompose, cloud render/TTS/STT/greenscreen, create, replicate)
|
|
685
696
|
respects it: minimize refuses billed spend without --yes and points you at the free
|
|
686
697
|
local path; hybrid/rich-ai run but print each op's cost. FREE local engines never gate
|
|
687
698
|
(local render, tts --engine local, stt --engine whisper, remove-greenscreen --local).
|
|
699
|
+
In minimize AND hybrid, 'vidfarm tts' DEFAULTS to the free local Kokoro voice — ask for
|
|
700
|
+
a premium voice (--style/--voice/--provider/--own-key/--cloud) or rich-ai to opt out.
|
|
688
701
|
|
|
689
702
|
Escape hatch — call ANY route directly:
|
|
690
703
|
api <METHOD> <path> Raw REST call with auth + pretty errors
|
|
@@ -1196,7 +1209,7 @@ function assertApiOk(result, action) {
|
|
|
1196
1209
|
const hint = result.status === 401 || result.status === 403
|
|
1197
1210
|
? " Check --api-key / VIDFARM_API_KEY and that you own this resource."
|
|
1198
1211
|
: result.status === 402
|
|
1199
|
-
? "
|
|
1212
|
+
? " This route is paid-only — your key is valid but the account is on the free plan (or the wallet is empty). Upgrade at /pricing, or keep working locally: `vidfarm serve` and the local render/decompose paths are free."
|
|
1200
1213
|
: result.status === 429
|
|
1201
1214
|
? " Rate-limited; wait and retry."
|
|
1202
1215
|
: "";
|
|
@@ -5133,6 +5146,35 @@ async function runTtsCommand(argv) {
|
|
|
5133
5146
|
await runLocalKokoroTts({ text, voice, style, out: parsed.values.out, format: explicitFormat, json });
|
|
5134
5147
|
return;
|
|
5135
5148
|
}
|
|
5149
|
+
// COST-MODE DEFAULT: in minimize/hybrid, narration starts on the FREE local
|
|
5150
|
+
// Kokoro engine unless the run asked for a premium/cloud voice (see
|
|
5151
|
+
// wantsPremiumVoice). The BYOK key path is a real fallback, not a downgrade —
|
|
5152
|
+
// if Kokoro is missing its python deps / model weights we fall THROUGH to it
|
|
5153
|
+
// below rather than failing the run.
|
|
5154
|
+
const costMode = resolveCostMode(parsed.values);
|
|
5155
|
+
const premium = wantsPremiumVoice(parsed.values, voice, style);
|
|
5156
|
+
/** Set when the cost-mode default ran Kokoro and it failed — so the keyless
|
|
5157
|
+
* path below doesn't pointlessly retry the same engine. */
|
|
5158
|
+
let kokoroError = null;
|
|
5159
|
+
if (!engine && !wantCloud && costMode.mode !== "pure-ai" && !premium) {
|
|
5160
|
+
try {
|
|
5161
|
+
await runLocalKokoroTts({
|
|
5162
|
+
text,
|
|
5163
|
+
voice,
|
|
5164
|
+
style,
|
|
5165
|
+
out: parsed.values.out,
|
|
5166
|
+
format: explicitFormat,
|
|
5167
|
+
json,
|
|
5168
|
+
costModeDefault: costMode.mode
|
|
5169
|
+
});
|
|
5170
|
+
return;
|
|
5171
|
+
}
|
|
5172
|
+
catch (error) {
|
|
5173
|
+
kokoroError = String(error?.message ?? error).split("\n")[0].slice(0, 200);
|
|
5174
|
+
// stderr, not stdout — keeps --json output parseable when we fall back.
|
|
5175
|
+
console.error(`${DIM}Local Kokoro engine unavailable (${kokoroError}) — falling back to your provider key / cloud TTS.${RESET}`);
|
|
5176
|
+
}
|
|
5177
|
+
}
|
|
5136
5178
|
if (!wantCloud) {
|
|
5137
5179
|
// A Gemini voice preset ("Kore", "Puck", …) should find a Gemini-capable
|
|
5138
5180
|
// key first — openrouter's default TTS model is Gemini-family too.
|
|
@@ -5148,6 +5190,14 @@ async function runTtsCommand(argv) {
|
|
|
5148
5190
|
const wanted = requestedProvider ?? `${voiceProvider} (implied by --voice "${voice}")`;
|
|
5149
5191
|
throw new Error(`tts: provider ${wanted} was requested but no matching key is configured. ${speechKeyHint("tts")}`);
|
|
5150
5192
|
}
|
|
5193
|
+
// Kokoro already ran and failed on the cost-mode default above — don't
|
|
5194
|
+
// retry the same broken engine, and don't dump its raw error. Name both
|
|
5195
|
+
// dead ends (no local deps, no key) with the two ways forward.
|
|
5196
|
+
if (kokoroError) {
|
|
5197
|
+
throw new Error(`tts: no provider key configured AND the free local Kokoro engine is unavailable (${kokoroError}). ` +
|
|
5198
|
+
"Fix one of the two: install the local engine (`pip install kokoro-onnx soundfile`, or point " +
|
|
5199
|
+
`HYPERFRAMES_PYTHON at a venv that has them), or ${speechKeyHint("tts")}`);
|
|
5200
|
+
}
|
|
5151
5201
|
// Keyless fallback (no explicit provider/voice): rather than erroring
|
|
5152
5202
|
// (speechKeyHint), speak through the local Kokoro engine so agents
|
|
5153
5203
|
// always get audio out of the box.
|
|
@@ -5237,17 +5287,41 @@ async function runTtsCommand(argv) {
|
|
|
5237
5287
|
console.log(`${GREEN}Wrote ${outPath}${RESET} ${DIM}(${mediaUrl})${RESET}`);
|
|
5238
5288
|
}
|
|
5239
5289
|
}
|
|
5290
|
+
/** Kokoro voice ids look like af_heart / am_adam / bf_emma. Anything else is a
|
|
5291
|
+
* cloud provider preset (openai "alloy", gemini "Kore", an ElevenLabs id).
|
|
5292
|
+
* Declared as a hoisted function, not a const: `wantsPremiumVoice` runs during
|
|
5293
|
+
* the top-level `main()` await, which is BEFORE this point in module order. */
|
|
5294
|
+
function isKokoroVoice(voice) {
|
|
5295
|
+
return /^[a-z]{2}_[a-z0-9]+$/i.test(voice);
|
|
5296
|
+
}
|
|
5297
|
+
/**
|
|
5298
|
+
* Did this run explicitly ask for a PREMIUM (cloud/BYOK) voice? If so, cost mode
|
|
5299
|
+
* must not silently route it to the free local engine — Kokoro has fixed voice
|
|
5300
|
+
* presets and no style direction, so honoring these flags locally would be a
|
|
5301
|
+
* silent downgrade. Any of: --provider, --model, --style, --own-key, or a
|
|
5302
|
+
* --voice that isn't a Kokoro preset.
|
|
5303
|
+
*/
|
|
5304
|
+
function wantsPremiumVoice(values, voice, style) {
|
|
5305
|
+
if (values.provider || values.model || values["own-key"])
|
|
5306
|
+
return true;
|
|
5307
|
+
if (style)
|
|
5308
|
+
return true;
|
|
5309
|
+
if (voice?.trim() && !isKokoroVoice(voice.trim()))
|
|
5310
|
+
return true;
|
|
5311
|
+
return false;
|
|
5312
|
+
}
|
|
5240
5313
|
// Keyless local TTS via the bundled hyperframes engine (Kokoro-82M ONNX —
|
|
5241
|
-
// free, no account, no HeyGen). Used by `--engine local
|
|
5242
|
-
//
|
|
5243
|
-
//
|
|
5314
|
+
// free, no account, no HeyGen). Used by `--engine local`, as the cost-mode
|
|
5315
|
+
// default in minimize/hybrid, and as the automatic fallback when no provider
|
|
5316
|
+
// key is configured. Kokoro emits WAV natively; an mp3 request transcodes
|
|
5317
|
+
// through the bundled ffmpeg.
|
|
5244
5318
|
async function runLocalKokoroTts(input) {
|
|
5245
5319
|
const wantMp3 = input.format === "mp3" || /\.mp3(\?|#|$)/i.test(input.out ?? "");
|
|
5246
5320
|
// Kokoro voice ids look like af_heart / am_adam / bf_emma. A provider voice
|
|
5247
5321
|
// (openai "alloy", gemini "Kore") passed through the fallback path would
|
|
5248
5322
|
// just error — drop it to the default with a note instead.
|
|
5249
5323
|
let voice = input.voice?.trim() || undefined;
|
|
5250
|
-
if (voice &&
|
|
5324
|
+
if (voice && !isKokoroVoice(voice)) {
|
|
5251
5325
|
if (!input.json)
|
|
5252
5326
|
console.log(`${DIM}Voice "${voice}" is a provider voice — the local Kokoro engine has its own presets (af_heart, af_nova, am_adam, bf_emma, …). Using the default voice.${RESET}`);
|
|
5253
5327
|
voice = undefined;
|
|
@@ -5258,8 +5332,12 @@ async function runLocalKokoroTts(input) {
|
|
|
5258
5332
|
const outPath = path.resolve(process.cwd(), input.out ?? `tts-${Date.now().toString(36)}.${wantMp3 ? "mp3" : "wav"}`);
|
|
5259
5333
|
mkdirSync(path.dirname(outPath), { recursive: true });
|
|
5260
5334
|
const wavPath = wantMp3 ? path.join(mkdtempSync(path.join(tmpdir(), "vidfarm-tts-")), "speech.wav") : outPath;
|
|
5261
|
-
if (!input.json)
|
|
5335
|
+
if (!input.json) {
|
|
5262
5336
|
console.log(`${DIM}Generating speech with the local Kokoro-82M engine (free, no account)…${RESET}`);
|
|
5337
|
+
if (input.costModeDefault) {
|
|
5338
|
+
console.log(`${DIM}(cost mode ${costModeDisplayName(input.costModeDefault)} → free local voice by default. Want a premium voice? add --style/--voice/--provider, or --cloud.)${RESET}`);
|
|
5339
|
+
}
|
|
5340
|
+
}
|
|
5263
5341
|
const run = await runHyperframesCommand("tts", [input.text, "-o", wavPath, ...(voice ? ["-v", voice] : [])], { stdio: input.json ? "capture" : "inherit" });
|
|
5264
5342
|
if (run.code !== 0 || !existsSync(wavPath)) {
|
|
5265
5343
|
const detail = run.stderr.trim().split("\n").pop() ?? "";
|
|
@@ -5276,7 +5354,7 @@ async function runLocalKokoroTts(input) {
|
|
|
5276
5354
|
}
|
|
5277
5355
|
const bytes = safeSize(outPath);
|
|
5278
5356
|
if (input.json) {
|
|
5279
|
-
printJson({ ok: true, mode: "local-engine", engine: "kokoro-82m", provider: null, voice: voice ?? "af_heart", fallback: Boolean(input.fallback), content_type: wantMp3 ? "audio/mpeg" : "audio/wav", bytes, out: outPath });
|
|
5357
|
+
printJson({ ok: true, mode: "local-engine", engine: "kokoro-82m", provider: null, voice: voice ?? "af_heart", fallback: Boolean(input.fallback), cost_mode_default: input.costModeDefault ?? null, content_type: wantMp3 ? "audio/mpeg" : "audio/wav", bytes, out: outPath });
|
|
5280
5358
|
}
|
|
5281
5359
|
else {
|
|
5282
5360
|
console.log(`${GREEN}Wrote ${outPath}${RESET} ${DIM}(local Kokoro-82M — free, no account, ${formatBytes(bytes)})${RESET}`);
|
|
@@ -5,12 +5,20 @@
|
|
|
5
5
|
//
|
|
6
6
|
// The mode is a single durable preference the human picks ONCE (ideally asked by
|
|
7
7
|
// their AI agent up front), then every billed command respects it:
|
|
8
|
-
// - minimize :
|
|
9
|
-
// refuse billed cloud/AI ops unless the user
|
|
10
|
-
//
|
|
11
|
-
//
|
|
12
|
-
//
|
|
13
|
-
// ops run
|
|
8
|
+
// - minimize : ~$0 per video. Never burn AI credits by surprise. Prefer free
|
|
9
|
+
// local engines; refuse billed cloud/AI ops unless the user
|
|
10
|
+
// re-confirms (--yes).
|
|
11
|
+
// - hybrid : roughly $0.01–$1 per video. Free where it's free, spend on AI
|
|
12
|
+
// only where it clearly wins. (default recommendation.) Billed
|
|
13
|
+
// ops run but print a cost line.
|
|
14
|
+
// - pure-ai : $1+ per video. Best-quality; AI image/video/voice/music used
|
|
15
|
+
// freely. Billed ops run; cost is still surfaced. Shown to
|
|
16
|
+
// humans as "rich-ai".
|
|
17
|
+
//
|
|
18
|
+
// Those dollar figures are what the AI providers charge, and they are billed to
|
|
19
|
+
// the user's OWN provider keys (BYOK — the keys saved via `vidfarm
|
|
20
|
+
// add-provider-key` / Settings → Bring your own keys), not to Vidfarm. Vidfarm
|
|
21
|
+
// wallet credits are only used when the user explicitly runs on the platform key.
|
|
14
22
|
//
|
|
15
23
|
// This module lives in the CLI's static import closure and is BACKEND-FREE (only
|
|
16
24
|
// Node built-ins) so it ships in the public cloud-only package.
|
|
@@ -101,18 +109,20 @@ export function resolveCostMode(values) {
|
|
|
101
109
|
return { mode: DEFAULT_COST_MODE, isSet: false, source: "default" };
|
|
102
110
|
}
|
|
103
111
|
export const COST_MODE_BLURB = {
|
|
104
|
-
minimize: "Minimize costs —
|
|
112
|
+
minimize: "Minimize costs — explicitly $0 videos. Stay on FREE local compute wherever possible (local render, " +
|
|
105
113
|
"local TTS/STT, local greenscreen/matting, reused clips + HTML motion). For assets, " +
|
|
106
114
|
'reach for the free stock catalog first — vidfarm media search "<meaning>" --type ' +
|
|
107
115
|
"bgm|sfx|image|vector|icon|video. Check the KEYLESS sources first: Openverse (CC/CC0 " +
|
|
108
116
|
"music, SFX, images) and iconify (icons) need no account at all. A free Pixabay key adds " +
|
|
109
117
|
"photos/vectors/stock-video and may already be saved (check vidfarm provider-keys). " +
|
|
110
118
|
"Billed cloud/AI generation is refused unless you re-confirm it (--yes).",
|
|
111
|
-
hybrid: "Hybrid (recommended) —
|
|
112
|
-
"clearly win (a hero shot, a voice you can't fake locally).
|
|
113
|
-
"each prints its cost so nothing is a surprise."
|
|
114
|
-
|
|
115
|
-
|
|
119
|
+
hybrid: "Hybrid (recommended) — roughly $0.01–$1 per video. Free where it's free, spend AI " +
|
|
120
|
+
"credits only where they clearly win (a hero shot, a voice you can't fake locally). " +
|
|
121
|
+
"Billed ops run but each prints its cost so nothing is a surprise. Charges land on " +
|
|
122
|
+
"your own AI provider keys (BYOK).",
|
|
123
|
+
"pure-ai": "Rich AI — $1+ per video. Best quality; AI image/video/voice/music used freely. " +
|
|
124
|
+
"Billed ops run without gating; cost is still shown. Charges land on your own AI " +
|
|
125
|
+
"provider keys (BYOK)."
|
|
116
126
|
};
|
|
117
127
|
/** One short human line summarizing the active mode. */
|
|
118
128
|
export function costModeSummaryLine(resolved) {
|
|
@@ -122,10 +132,12 @@ export function costModeSummaryLine(resolved) {
|
|
|
122
132
|
/** The 3-line "explain it simply" block an agent should relay to the user. */
|
|
123
133
|
export function costModeExplainer() {
|
|
124
134
|
return [
|
|
125
|
-
"How much do you want Vidfarm to spend on AI
|
|
126
|
-
" • minimize —
|
|
127
|
-
" • hybrid — recommended: free where free, pay AI only where it clearly wins.",
|
|
128
|
-
" • rich-ai — best quality
|
|
135
|
+
"How much do you want Vidfarm to spend on AI per video?",
|
|
136
|
+
" • minimize — $0 videos: free local compute + free stock media, no AI spend at all.",
|
|
137
|
+
" • hybrid — ~$0.01–$1 per video (recommended): free where free, pay AI only where it clearly wins.",
|
|
138
|
+
" • rich-ai — $1+ per video: best quality, use AI image/video/voice/music freely.",
|
|
139
|
+
"Any spend is billed to YOUR own AI provider keys (BYOK) — add them with",
|
|
140
|
+
" `vidfarm add-provider-key <provider> <key>` or at vidfarm.cc/settings/developer.",
|
|
129
141
|
"Tip: before paying to generate music/SFX/images/video, try the free stock catalog —",
|
|
130
142
|
' vidfarm media search "<meaning>" --type bgm|sfx|image|vector|icon|video.',
|
|
131
143
|
" Check the keyless sources first — Openverse (CC/CC0 music, SFX, images) and iconify (icons)",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@officexapp/vidfarm-devcli",
|
|
3
|
-
"version": "0.21.
|
|
3
|
+
"version": "0.21.26",
|
|
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": {
|
|
@@ -51,6 +51,7 @@
|
|
|
51
51
|
"SKILL.md",
|
|
52
52
|
"SKILL.director.md",
|
|
53
53
|
"clipper.md",
|
|
54
|
+
"update.md",
|
|
54
55
|
"!readme.secret.md",
|
|
55
56
|
"!**/*.secret.*"
|
|
56
57
|
],
|
|
@@ -65,7 +66,7 @@
|
|
|
65
66
|
"node": ">=22.0.0"
|
|
66
67
|
},
|
|
67
68
|
"scripts": {
|
|
68
|
-
"
|
|
69
|
+
"prepare": "patch-package || true",
|
|
69
70
|
"dev": "tsx --import ./src/instrument.ts watch src/index.ts",
|
|
70
71
|
"dev:frontend": "node scripts/build-homepage-client.mjs --watch",
|
|
71
72
|
"dev:cli": "tsx src/cli.ts",
|
package/update.md
ADDED
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: vidfarm-update
|
|
3
|
+
description: How to update Vidfarm — clean-uninstall the `vidfarm` agent skill and the `vidfarm-devcli`, then reinstall the latest of both from vidfarm.cc/skill.md and npm. Read this whenever the user asks to update/upgrade Vidfarm, says their vidfarm skill or devcli is out of date, hits a command or route that "used to work", or gets a version-mismatch or unknown-command error. Also carries breaking-change notes per version — check it before assuming an old workflow still applies.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Updating Vidfarm
|
|
7
|
+
|
|
8
|
+
**Read this file live at <https://vidfarm.cc/update.md> every time you update — don't work from a
|
|
9
|
+
cached copy.** It carries the current breaking-change notes, and an update that skips them is how a
|
|
10
|
+
working setup quietly breaks.
|
|
11
|
+
|
|
12
|
+
Vidfarm ships as two moving parts, and they are versioned together:
|
|
13
|
+
|
|
14
|
+
1. **The `vidfarm` agent skill** — the director workflows on disk, installed under
|
|
15
|
+
`.agents/skills/vidfarm/` with a `.claude/skills/vidfarm` symlink.
|
|
16
|
+
2. **The `vidfarm-devcli`** — the `vidfarm` command, published as `@officexapp/vidfarm-devcli`.
|
|
17
|
+
|
|
18
|
+
Updating one but not the other is the most common cause of "this command doesn't exist" and "the
|
|
19
|
+
skill says to do X but it fails" — the skill documents routes and flags the installed CLI may not
|
|
20
|
+
have yet. **Always update both together.**
|
|
21
|
+
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
## The update, in four steps
|
|
25
|
+
|
|
26
|
+
### 1. Record what they're on now
|
|
27
|
+
|
|
28
|
+
Worth ten seconds, because it tells you which breaking-change notes below actually apply:
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
vidfarm --version # devcli version
|
|
32
|
+
npm view @officexapp/vidfarm-devcli version # latest published
|
|
33
|
+
cat skills-lock.json # which skill packs are installed, and from where
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
### 2. Remove the old skill
|
|
37
|
+
|
|
38
|
+
There is no `skills remove` command — a skill is just a directory plus a lock entry, so delete both.
|
|
39
|
+
Run this from the project root (the directory holding `.agents/skills`):
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
rm -rf .agents/skills/vidfarm .claude/skills/vidfarm
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Then drop `"vidfarm"` from the `skills` object in `skills-lock.json` (leave any other packs alone —
|
|
46
|
+
`hyperframes`, `vidfarm-media`, etc. are separate entries and other tools write to this file too).
|
|
47
|
+
|
|
48
|
+
A clean removal matters more than it looks: `skills add` overwrites files it knows about, so a file
|
|
49
|
+
that was *deleted upstream* survives an in-place refresh and keeps feeding the agent stale
|
|
50
|
+
instructions. Deleting the directory is what guarantees you end up with exactly the current pack.
|
|
51
|
+
|
|
52
|
+
### 3. Reinstall the latest of both
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
npm i -g @officexapp/vidfarm-devcli@latest # the CLI
|
|
56
|
+
vidfarm skills add vidfarm # the skill pack, fetched from vidfarm.cc/skill-pack/*
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
`vidfarm skills update` refreshes every installed pack in place — that's the quick path when nothing
|
|
60
|
+
has been removed upstream. The uninstall-then-add above is the thorough one; prefer it when the user
|
|
61
|
+
is troubleshooting, is several versions behind, or when a note below says to.
|
|
62
|
+
|
|
63
|
+
If the user also has the HyperFrames engine installed, bring it along — Vidfarm renders *are*
|
|
64
|
+
HyperFrames renders, and a version skew between them shows up as render failures:
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
npm i -g hyperframes@latest
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
### 4. Verify before declaring success
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
vidfarm --version # should match the npm latest from step 1
|
|
74
|
+
vidfarm doctor # ffmpeg + hyperframes + Chrome + auth, all in one check
|
|
75
|
+
vidfarm whoami # confirms the API key survived the reinstall, prints the active cost mode
|
|
76
|
+
vidfarm skills list # confirms the vidfarm pack is present, and from which source
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
`vidfarm doctor` is the real test — a version number proves the package installed, not that the
|
|
80
|
+
toolchain works. If `whoami` fails, the login just needs redoing: `vidfarm login <email>`, or
|
|
81
|
+
`vidfarm login --api-key "$VIDFARM_API_KEY"`. Credentials live in `~/.vidfarm/` and normally survive
|
|
82
|
+
an update untouched.
|
|
83
|
+
|
|
84
|
+
Then **re-read <https://vidfarm.cc/skill.md>** and follow it to the director skill. The freshly
|
|
85
|
+
installed pack may route differently than the one you loaded at the start of the session.
|
|
86
|
+
|
|
87
|
+
---
|
|
88
|
+
|
|
89
|
+
## Breaking changes and version notes
|
|
90
|
+
|
|
91
|
+
Check this section against the version from step 1 and apply anything between their version and the
|
|
92
|
+
latest. If nothing here mentions their version, a plain update is safe.
|
|
93
|
+
|
|
94
|
+
**Current published devcli: `0.21.23`.**
|
|
95
|
+
|
|
96
|
+
- **0.21.x — the package moved scopes.** The devcli is `@officexapp/vidfarm-devcli`. The old
|
|
97
|
+
`@mevdragon/vidfarm-devcli` is not maintained. If `npm ls -g` shows the `@mevdragon` package,
|
|
98
|
+
uninstall it explicitly, or two `vidfarm` binaries will fight over the same name on `PATH`:
|
|
99
|
+
|
|
100
|
+
```bash
|
|
101
|
+
npm rm -g @mevdragon/vidfarm-devcli
|
|
102
|
+
npm i -g @officexapp/vidfarm-devcli@latest
|
|
103
|
+
which vidfarm && vidfarm --version # confirm the survivor is the new one
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
- **`update-skill` is retired.** It now warns and delegates. Use `vidfarm skills add vidfarm`.
|
|
107
|
+
`--global` and `--platform` are gone; scope an install with `--dir <project-root>` instead.
|
|
108
|
+
|
|
109
|
+
- **0.6.0 and earlier `vidfarm serve` is broken when installed from npm** (the tarball shipped
|
|
110
|
+
without its runtime). Anyone still on 0.5.x must update — there is no workaround.
|
|
111
|
+
|
|
112
|
+
- **Cost mode is remembered across updates.** It lives in `~/.vidfarm/cost-mode.json`, outside the
|
|
113
|
+
package, so an update never silently changes their spending posture. Confirm it with `vidfarm
|
|
114
|
+
whoami` anyway and say what it is out loud — it decides whether billed commands are refused.
|
|
115
|
+
|
|
116
|
+
---
|
|
117
|
+
|
|
118
|
+
## If the update leaves things broken
|
|
119
|
+
|
|
120
|
+
| Symptom | Cause | Fix |
|
|
121
|
+
|---|---|---|
|
|
122
|
+
| `vidfarm: command not found` | Global bin not on `PATH`, or the install failed | `npm ls -g --depth=0`, then check `npm bin -g` is on `PATH` |
|
|
123
|
+
| Wrong version after installing | An older `@mevdragon` copy is shadowing it | `npm rm -g @mevdragon/vidfarm-devcli`, then `which vidfarm` |
|
|
124
|
+
| `EACCES` on global install | npm's global dir needs root | Use a Node version manager (nvm/fnm) rather than `sudo npm` |
|
|
125
|
+
| Skill still shows old instructions | The directory wasn't actually removed | Redo step 2, confirm `.agents/skills/vidfarm` is gone, then `skills add` |
|
|
126
|
+
| `skills add` fails to fetch | Offline or the host is unreachable | It falls back to copies bundled in the installed package — check `vidfarm skills list` for the source it used |
|
|
127
|
+
| `401` / `whoami` fails | Credential not carried over | `vidfarm login <email>` (email code), or `--api-key` |
|
|
128
|
+
| Renders fail after updating | devcli / hyperframes version skew | `npm i -g hyperframes@latest`, then `vidfarm doctor` |
|
|
129
|
+
| A documented route 404s | Skill is newer than the deployed backend | Report it — don't work around it by calling the renderer or an internal route directly |
|
|
130
|
+
|
|
131
|
+
**Tell the user plainly what changed.** After updating, say which versions they went from and to,
|
|
132
|
+
and name anything from the breaking-changes section that affects them. If the update failed partway,
|
|
133
|
+
say so with the error rather than reporting success — a half-updated toolchain fails later, in the
|
|
134
|
+
middle of a render, where it's much harder to diagnose.
|
|
135
|
+
|
|
136
|
+
---
|
|
137
|
+
|
|
138
|
+
## More
|
|
139
|
+
|
|
140
|
+
- Entry skill (start here after updating): <https://vidfarm.cc/skill.md>
|
|
141
|
+
- Full director skill: <https://vidfarm.cc/skill/vidfarm>
|
|
142
|
+
- Skill pack index (what else is installable): <https://vidfarm.cc/skill-pack/index.json>
|
|
143
|
+
- Agentic clipper loop: <https://vidfarm.cc/clipper.md>
|