@officexapp/vidfarm-devcli 0.21.11 → 0.21.14
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/hyperframes-core/SKILL.md +2 -0
- package/.agents/skills/hyperframes-creative/SKILL.md +1 -0
- package/.agents/skills/hyperframes-creative/references/beat-direction.md +17 -0
- package/.agents/skills/vidfarm-director/SKILL.md +4 -0
- package/.agents/skills/vidfarm-director/recipes/local-edit-render-approve.md +1 -1
- package/.agents/skills/vidfarm-director/references/automation-and-local-dev.md +23 -6
- package/.agents/skills/vidfarm-director/references/core-workflows.md +1 -1
- package/.agents/skills/vidfarm-director/references/editor-workflows.md +15 -2
- package/.agents/skills/vidfarm-director/references/primitives.md +1 -1
- package/.agents/skills/vidfarm-director/references/rest-api.md +1 -1
- package/.agents/skills/vidfarm-media/SKILL.md +16 -6
- package/README.md +2 -2
- package/SKILL.director.md +45 -11
- package/demo/dist/app.js +247 -226
- package/dist/src/cli.js +198 -30
- package/dist/src/devcli/composition-edit.js +99 -23
- package/dist/src/devcli/cost-mode.js +13 -4
- package/dist/src/devcli/doctor.js +65 -9
- package/dist/src/devcli/local-frontend-server.js +342 -50
- package/dist/src/devcli/port-utils.js +43 -0
- package/dist/src/devcli/process-scan.js +173 -0
- package/package.json +3 -1
- package/public/serve-shells/editor.html +75 -13
- package/public/serve-shells/library-files.html +75 -13
- package/public/serve-shells/library-raws.html +75 -13
- package/public/serve-shells/tools-clipper.html +75 -13
- package/public/serve-shells/tools-image.html +75 -13
- package/public/serve-shells/tools-video.html +75 -13
|
@@ -48,6 +48,8 @@ The standalone root needs an explicit **sized box** (`width`/`height` in px), an
|
|
|
48
48
|
|
|
49
49
|
Each composition registers **exactly one** `gsap.timeline({ paused: true })` at `window.__timelines["<id>"]` (key = root `data-composition-id`), built **synchronously** at page load. Render duration = root `data-duration`, not timeline length. Don't manually nest sub-timelines into the host. Full contract (incl. non-GSAP runtimes) → `references/determinism-rules.md` + `hyperframes-animation/adapters/`.
|
|
50
50
|
|
|
51
|
+
Because the timeline is paused and seek-safe, **the frame at t=0 is whatever the DOM computes at time 0** — and that frame doubles as the video's preview thumbnail (autoplay-off feeds, share cards, `<video>` poster, file/scrubber thumbnails). If the opening scene animates its elements in from `opacity:0`/offset, t=0 is a blank/black poster that nobody clicks. Design the opening scene's initial state as a real, interesting still. Craft guidance → `hyperframes-creative/references/beat-direction.md` ("The opening frame is the poster").
|
|
52
|
+
|
|
51
53
|
### Non-negotiable rules (silent bugs `lint`/`validate`/`inspect` won't catch)
|
|
52
54
|
|
|
53
55
|
Surfaced here; full rationale in the linked reference. Do not violate:
|
|
@@ -40,6 +40,7 @@ For motion patterns, scene blueprints, transitions, and CSS marker effects, use
|
|
|
40
40
|
| Structured expansion for open-ended prompts | `references/prompt-expansion.md` |
|
|
41
41
|
| Video-medium density, scale, color, frame composition | `references/video-composition.md` |
|
|
42
42
|
| Per-beat direction, rhythm planning, transition timing | `references/beat-direction.md` |
|
|
43
|
+
| Opening/first frame as the preview thumbnail (t=0 must not be empty) | `references/beat-direction.md` |
|
|
43
44
|
| Post-authoring spec verification (colors, type, corners, spacing, depth) | `references/design-adherence.md` |
|
|
44
45
|
| High-level motion guardrails and GSAP-quality rules | `references/motion-principles.md` |
|
|
45
46
|
| Font selection, pairings, rendered-video type guardrails | `references/typography.md` |
|
|
@@ -4,6 +4,7 @@ How to plan and direct individual scenes (beats) in a multi-scene composition. R
|
|
|
4
4
|
|
|
5
5
|
## Contents
|
|
6
6
|
|
|
7
|
+
- The opening frame is the poster
|
|
7
8
|
- Per-beat direction
|
|
8
9
|
- Concept
|
|
9
10
|
- Mood direction
|
|
@@ -16,6 +17,22 @@ How to plan and direct individual scenes (beats) in a multi-scene composition. R
|
|
|
16
17
|
|
|
17
18
|
---
|
|
18
19
|
|
|
20
|
+
## The Opening Frame Is the Poster
|
|
21
|
+
|
|
22
|
+
**The very first frame (t=0) is the video's preview thumbnail** — it's the still the viewer stares at *before* they press play (feed autoplay-off, share cards, `<video>` poster, file thumbnails, the editor's timeline scrubber at 0). A blank, black, or half-built opening frame is a dead thumbnail: nobody presses play on empty. Treat t=0 as a designed poster that has to sell the click.
|
|
23
|
+
|
|
24
|
+
The trap is mechanical: most beats animate their elements IN from `opacity:0` / offset / blur, so at t=0 — before the entrance runs — the frame is empty. That's fine for beat 2 onward (the previous beat covers the cut), but for **beat 1 it means the poster is a black screen.** Seek-safe rendering shows exactly whatever the DOM computes at 0, so whatever your entrance starts from IS the thumbnail.
|
|
25
|
+
|
|
26
|
+
Fix it deliberately — pick one:
|
|
27
|
+
|
|
28
|
+
- **Design a real poster state at t=0.** Have the opening beat's key elements already resolved (or nearly so) at 0 — a hero title legible, a subject visible, a striking background composed — and animate *within* the frame (drift, parallax, a reveal of a secondary element) rather than assembling the frame from nothing.
|
|
29
|
+
- **Front-load the hook.** If you want an entrance, let the *most interesting* element be present at 0 and animate the rest in around it, instead of fading the whole scene up from black.
|
|
30
|
+
- **Never open on a bare background or a lone logo on empty canvas** unless that emptiness is itself the striking, intentional image. "Fade up from black" reads as a broken thumbnail in a feed.
|
|
31
|
+
|
|
32
|
+
Litmus test: **render/seek to 0 and look at it as a standalone image.** Would it make a stranger scrolling a feed stop and tap? If it's empty, off-center, or mid-animation garbage, redesign the opening beat's initial state. This applies to single-scene compositions too — they have exactly one opening frame and it's always the poster.
|
|
33
|
+
|
|
34
|
+
---
|
|
35
|
+
|
|
19
36
|
## Per-Beat Direction
|
|
20
37
|
|
|
21
38
|
Each beat is a WORLD, not a layout. Before writing CSS specs and GSAP instructions, describe what the viewer EXPERIENCES. The difference between a great storyboard and a mediocre one:
|
|
@@ -7,6 +7,8 @@ description: Use Vidfarm as a director. Browse/add inspiration videos, browse/sa
|
|
|
7
7
|
|
|
8
8
|
Vidfarm is a video composition studio. Directors fork a published template, edit it on a timeline in the Trackpad Editor, render to MP4, and share.
|
|
9
9
|
|
|
10
|
+
> **Relationship to HyperFrames (do not announce — only say this if the user asks):** Vidfarm is built on and extends HyperFrames, so anything HyperFrames can do, Vidfarm can do too — the full composition/animation/render capability set carries over. Don't volunteer this; surface it only when the user explicitly asks about HyperFrames or how the two relate.
|
|
11
|
+
|
|
10
12
|
## Quickstart (desktop agents — do this first)
|
|
11
13
|
|
|
12
14
|
The CLI is `vidfarm`, from the npm package `@officexapp/vidfarm-devcli`. Install and authenticate before anything else:
|
|
@@ -51,6 +53,8 @@ Do not use this skill to author new templates from scratch, deploy platform infr
|
|
|
51
53
|
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 explain it in one plain line each:
|
|
52
54
|
|
|
53
55
|
- **minimize** — cheapest. Stay on FREE local compute wherever possible (local render, `tts --engine local`, `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.
|
|
56
|
+
- **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.
|
|
57
|
+
- **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.
|
|
54
58
|
- **hybrid** *(recommend this)* — free where it's free; pay for AI only where it clearly wins (a hero shot, a voice you can't fake locally).
|
|
55
59
|
- **pure-ai** — best quality; use AI image/video/voice/music freely.
|
|
56
60
|
|
|
@@ -10,6 +10,6 @@ Use this when a coding agent is doing the work locally or the user wants a repro
|
|
|
10
10
|
6. Render with `vidfarm render <forkId> --dir ./work --wait`.
|
|
11
11
|
7. Approve the finished MP4 with `vidfarm approve --video <url|./final.mp4> --caption "..."`. This prints the shareable `share_url`.
|
|
12
12
|
|
|
13
|
-
**Approving a locally rendered file → cloud preview link.** Approve takes media by **URL**, not bytes
|
|
13
|
+
**Approving a locally rendered file → cloud preview link.** Approve takes media by **URL**, not bytes, and an approved post is **permanent** — so the local MP4 must land in **durable My Files**, not the 30-day temp store (a temp video would 404 the share page after 30 days). The devcli presigns, PUTs the bytes **direct to S3**, finalizes, then approves with that durable URL — so `vidfarm approve --video ./final.mp4` handles files up to **200 MB**, bypasses the ~6 MB Lambda request-body limit, and the share link never breaks. By raw REST: `POST /api/v1/user/me/attachments/presign` → PUT to the returned S3 URL → `POST /api/v1/user/me/attachments` (finalize) → pass the returned `viewUrl` in the approve `media` array. Do not multipart-POST a big file to `.../attachments/upload` against the cloud host (Lambda-bound, ~6 MB cap). Add `vidfarm approve --temp` only when you want a disposable 30-day preview.
|
|
14
14
|
|
|
15
15
|
Prefer this path for batch work, CI-like edits, or when the user wants free local rendering through `vidfarm serve`.
|
|
@@ -148,7 +148,7 @@ If a local AI script rewrites text or scenes without consuming those files first
|
|
|
148
148
|
| `vidfarm visibility <forkId> <private\|public>` | `PATCH .../compositions/:forkId/visibility` | set visibility |
|
|
149
149
|
| `vidfarm clone <forkId>` | `POST .../compositions/:forkId/clone` | clone a fork |
|
|
150
150
|
| `vidfarm share-link <forkId>` | `POST .../compositions/:forkId/share-links` | mint a share URL |
|
|
151
|
-
| `vidfarm approve --video <url\|file> --caption "…"` | `POST /api/v1/approved/posts` | approve post — takes media by **`url`**. Local `--video`/`--media` auto-upload via presign→S3→finalize (up to **200 MB**, bypasses the ~6 MB Lambda limit) then approve with that URL
|
|
151
|
+
| `vidfarm approve --video <url\|file> --caption "…"` | `POST /api/v1/approved/posts` | approve post — takes media by **`url`**. Local `--video`/`--media` auto-upload to **durable My Files** via presign→S3→finalize (up to **200 MB**, bypasses the ~6 MB Lambda limit) then approve with that URL, so the share video **never expires**; also accepts a `/files…` path. `--temp` opts into the 30-day temp store (throwaway preview only). Prints `share_url` |
|
|
152
152
|
| `vidfarm posts` / `vidfarm post <id>` | `GET /api/v1/approved/posts[/:id]` | browse approved posts |
|
|
153
153
|
| `vidfarm schedule <postId> --at <iso> --to <dest>` | `POST /api/v1/approved/posts/:postId/schedules` | schedule a post |
|
|
154
154
|
| `vidfarm schedules <postId>` | `GET /api/v1/approved/posts/:postId/schedules` | browse scheduled posts |
|
|
@@ -173,7 +173,7 @@ If a local AI script rewrites text or scenes without consuming those files first
|
|
|
173
173
|
| `vidfarm raws preset list\|run\|save` / `raws export <ids…> --to <dir>` | (local library) | saved queries; copy raw MP4s out |
|
|
174
174
|
| `vidfarm lint <dir\|composition.html>` | (local static validation) | pre-publish composition check: timing, overlaps, preset names, media src |
|
|
175
175
|
| `vidfarm stills <dir> [--at 0,2.5,…]` | (local in-process render of PNG frames) | visually verify an edit without a full render |
|
|
176
|
-
| `vidfarm doctor` | (local environment triage) | check ffmpeg/node/keys/agent CLI/poisoned env before debugging anything else |
|
|
176
|
+
| `vidfarm doctor` | (local environment triage) | check ffmpeg/node/keys/agent CLI/poisoned env + list local serve/preview processes before debugging anything else; `--kill-orphans` reaps dead servers squatting ports (fixes the "Waiting for preview server…" hang) |
|
|
177
177
|
| `vidfarm skills list\|add <name>\|update` | `GET /skill-pack/index.json` · `/skill-pack/:name/*` | install/refresh skill packs (see "Skill packs — import on demand") |
|
|
178
178
|
| `vidfarm tts "…" --engine local` / `vidfarm stt <file> --engine whisper` | (keyless LOCAL engines: Kokoro-82M TTS, whisper.cpp STT) | narration + word-timestamp transcripts with zero keys and zero accounts |
|
|
179
179
|
| `vidfarm remove-background <video\|image>` | (local ONNX matting — free) | transparent-subject media for occlusion captions/cutouts (arbitrary/messy background; for a FLAT solid background use `remove-background-greenscreen`) |
|
|
@@ -184,13 +184,13 @@ If a local AI script rewrites text or scenes without consuming those files first
|
|
|
184
184
|
|
|
185
185
|
**Uploads/downloads/My-Files writes live in the devcli** because they are genuine multi-step / streaming flows: `upload` presigns the **ephemeral** temporary-files route, PUTs the bytes **direct to S3**, finalizes, and prints the durable URL to drop into a composition or approved post — going direct to S3 means large files (up to **200 MB**) never hit the ~6 MB Lambda request-body limit; `put-file` posts to the **persistent** My Files route (`/me/attachments/upload`) so context docs and brand assets live in the user's library (accepts a local file, `--content <text>`, or piped `--stdin` with `--as <name>`); `download` streams any Vidfarm/media URL to disk.
|
|
186
186
|
|
|
187
|
-
**Approving a locally rendered MP4 (the URL-first rule).** The approve route (`POST /api/v1/approved/posts`) and every media-taking route accept a `url`, never raw file bytes — so you never "upload to approve" in one shot. The correct sequence for a big local file is: (1) `POST /api/v1/user/me/
|
|
187
|
+
**Approving a locally rendered MP4 (the URL-first rule + the durability rule).** The approve route (`POST /api/v1/approved/posts`) and every media-taking route accept a `url`, never raw file bytes — so you never "upload to approve" in one shot. An approved post is a **permanent** share page, so the media must live in **durable My Files**, not the 30-day temp store (a temp-hosted video would 404 the share page after 30 days). The correct sequence for a big local file is: (1) `POST /api/v1/user/me/attachments/presign` with `{ file_name, content_type, size_bytes }` → (2) PUT the raw bytes to the returned presigned S3 URL → (3) `POST /api/v1/user/me/attachments` (finalize) → use the returned durable `viewUrl` as the approve media `url`. `vidfarm approve --video ./final.mp4` does all of this automatically (durable by default; up to **200 MB**). Never POST a large file as multipart to `.../attachments/upload` against the cloud host: that path proxies through Lambda and caps near 6 MB (it exists only as a fallback for local-storage `vidfarm serve` boxes). Only use the temp-store route (`.../temporary-files/*`, or `vidfarm approve --temp`) for a **throwaway** preview you don't mind losing in 30 days.
|
|
188
188
|
|
|
189
189
|
## Cost mode — the devcli's money-saving guardrail
|
|
190
190
|
|
|
191
191
|
`vidfarm cost-mode <minimize|hybrid|pure-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`, 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`, 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.
|
|
192
192
|
|
|
193
|
-
- **minimize** — a billed op is **refused** unless you add `--yes`; the error names the free local alternative. 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.
|
|
193
|
+
- **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.
|
|
194
194
|
- **hybrid** *(default recommendation)* — billed ops run but print a one-line cost notice each.
|
|
195
195
|
- **pure-ai** — billed ops run without gating; cost is still printed.
|
|
196
196
|
|
|
@@ -211,7 +211,7 @@ npx -y @officexapp/vidfarm-devcli serve <template_id>
|
|
|
211
211
|
|
|
212
212
|
The **editor and its data** run locally (`RECORDS_DRIVER=local`, `STORAGE_DRIVER=local`) and rendering happens **in-process on this box for free**. Everything catalog-shaped still mirrors the cloud host ("cloud passthrough"): `/discover`, `/api/v1/videos`, and `/library` list the **cloud** catalog and the cloud account's approved posts next to anything local, opening a cloud template **seeds** its composition onto disk on demand, Add Template ingests into the cloud account, and approved-post actions (schedules, archive, delete) on cloud posts proxy through. Media a seeded composition already references stays on its cloud URLs — but you can also drop your **own local files** onto the timeline without any upload: `vidfarm place --src ./clip.mp4` copies the file into this box's disk store and references it by a `localhost/storage` URL (see "Local file paths as media" above), so the free in-process renderer plays it back with zero S3 involvement. Pass `--no-cloud` for a fully-offline box.
|
|
213
213
|
|
|
214
|
-
**Rendering from a serve box** — the editor's **Render** button becomes a popover with two options when a cloud `--api-key` is configured: **Render Local (Free)** (in-process
|
|
214
|
+
**Rendering from a serve box** — the editor's **Render** button becomes a popover with two options when a cloud `--api-key` is configured: **Render Local (Free)** (in-process Vidfarm render, no charge) and **Render in Cloud** (hands the render to the cloud renderer, billed to the cloud account's wallet). Over REST, pass `render_target: "cloud"` in the `POST /render` body; the local box resolves (or clones, once) a publishable cloud fork, remembers the mapping in the fork's `upstream-link.json`, and `GET /renders/:renderId` transparently proxies the cloud job status.
|
|
215
215
|
|
|
216
216
|
How the loop works:
|
|
217
217
|
- The composition lives on disk at `<data-dir>/storage/compositions/forks/<forkId>/working/composition.html` (default `<data-dir>` is `./.vidfarm-local`). Point your agent at that file.
|
|
@@ -220,7 +220,24 @@ How the loop works:
|
|
|
220
220
|
|
|
221
221
|
Seeding from the cloud: `serve <template_id>` pulls the template's **default fork** (which may be another user's public decomposition); `serve --fork <id>` pulls a specific fork. Only the composition + records are localized — media stays on cloud URLs. Re-serving keeps local edits unless you pass `--refetch`.
|
|
222
222
|
|
|
223
|
-
Flags: `--port` (default 3000), `--dir` (default `./.vidfarm-local`), `--key` (bootstrap/browser key, or `VIDFARM_API_KEY`), `--fork <id>`, `--host` (cloud host to mirror + pull from, default `https://vidfarm.cc`), `--api-key` (cloud key for pulls, `/library`, and cloud render — defaults to `VIDFARM_API_KEY`), `--refetch`, `--no-cloud` (fully offline: no cloud catalog, seeding, or cloud render), `--no-open`. `vidfarm <template_id>` is an alias for `serve <template_id>`.
|
|
223
|
+
Flags: `--port` (default 3000; **auto-advances to the next free port** so a second/third job never collides), `--dir` (default `./.vidfarm-local`), `--key` (bootstrap/browser key, or `VIDFARM_API_KEY`), `--fork <id>`, `--host` (cloud host to mirror + pull from, default `https://vidfarm.cc`), `--api-key` (cloud key for pulls, `/library`, and cloud render — defaults to `VIDFARM_API_KEY`), `--refetch`, `--no-cloud` (fully offline: no cloud catalog, seeding, or cloud render), `--no-open`. `vidfarm <template_id>` is an alias for `serve <template_id>`.
|
|
224
|
+
|
|
225
|
+
### Running several local jobs at once (and cleaning up orphans)
|
|
226
|
+
|
|
227
|
+
Customers routinely run more than one local video job in parallel — one `vidfarm serve` per fork. This is safe and expected:
|
|
228
|
+
|
|
229
|
+
- **Ports auto-resolve.** `serve` claims the next free port at or above `--port`, so the 1st box lands on `3000`, the 2nd on `3001`, and so on. It prints `using <port> instead` when it moves, and never crashes with `EADDRINUSE`. To pin a specific port, pass `--port <n>`; if that exact port is taken it still advances (with a warning) rather than failing.
|
|
230
|
+
- **Give each job its own data dir.** Concurrent boxes editing the *same* fork should not share a `--dir`, or their disk edits race. Use a distinct `--dir ./work-<n>` per job: `vidfarm serve <tpl> --fork <fork> --port 3000 --dir ./job-a` alongside `... --port 3001 --dir ./job-b`. Different forks under **one** server co-edit fine (the loop suppresses self-writes); it's the *same fork in two servers* that collides.
|
|
231
|
+
- **Reference the printed URL, not `localhost:3000`.** When a box moves to `3001`, its media and reference URLs are only valid on *that* origin — pass `--base-url http://localhost:<port>` to `place` for off-disk media (see "Local file paths as media").
|
|
232
|
+
|
|
233
|
+
**Orphaned servers.** If the devcli is upgraded, renamed, or its package dir is pruned while a `serve` / `hyperframes preview` is still running, that process keeps holding its port but now runs from a **deleted path** — it serves broken assets forever. This is the classic `/editor` **"Waiting for preview server… run npm run dev"** hang: the preview backend is a dead orphan. Diagnose and reclaim:
|
|
234
|
+
|
|
235
|
+
```bash
|
|
236
|
+
vidfarm doctor # the "local servers" line lists every serve/preview + flags ⚠ORPHAN (binary deleted)
|
|
237
|
+
vidfarm doctor --kill-orphans # SIGTERM (then SIGKILL) every orphaned server and free its port
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
`--kill-orphans` only reaps processes whose launch script no longer exists on disk (never a live, healthy server, never the current process). After reaping, re-launch `serve` from the current install. If a *healthy* server is holding the port you want, just let `serve` pick the next free one (or stop that job first).
|
|
224
241
|
|
|
225
242
|
To *analyze* the source media locally (videos, transcript, recurring cast), read the `video-context.json` / `cast.json` routes, or pull a media URL with `vidfarm download <url>`.
|
|
226
243
|
|
|
@@ -260,7 +260,7 @@ There is no single price for a video. The **approach** the director picks sets t
|
|
|
260
260
|
|
|
261
261
|
| Approach | Typical cost | How |
|
|
262
262
|
|---|---|---|
|
|
263
|
-
| **Reuse + render locally** | **free** | Fork an already-decomposed template, swap captions / images / video with existing MP4s (from **My Files**, the director's **local computer** — reference files straight off disk with `place --src ./file`, no upload — or a **web search**), and render **locally** via `vidfarm serve` (native in-process
|
|
263
|
+
| **Reuse + render locally** | **free** | Fork an already-decomposed template, swap captions / images / video with existing MP4s (from **My Files**, the director's **local computer** — reference files straight off disk with `place --src ./file`, no upload — or a **web search**), and render **locally** via `vidfarm serve` (native in-process Vidfarm render — free and unguarded, no cloud). |
|
|
264
264
|
| **Reuse + cloud render** | **~$0.001 – $0.03** | Same reuse, but render on the cloud renderer (`POST /compositions/:forkId/render`, ~$0.01–$0.10 depending on length/res). Cheap **image** generation/edits fit in this band too. |
|
|
265
265
|
| **AI-generate some scenes** | **~$1** | Replace a few scenes with AI-generated video clips for high specificity/customization (see the "Generate AI media" section). |
|
|
266
266
|
| **Heavy AI generation** | **$10+** | Many/long AI video clips, custom characters, fully bespoke scenes. |
|
|
@@ -291,6 +291,19 @@ Two surfaces:
|
|
|
291
291
|
- **Editor web/serve copilot** — `editor_action` `action_type=set_layer_media` with `object_fit` and `object_position` (both also seedable on `add_layer`/`generate_layer`). `editor_context.layers[]` reports each clip's current `object_fit`/`object_position` so you can read before re-cropping.
|
|
292
292
|
- **Desktop agents (devcli)** — `vidfarm place ./work --src <url> --object-fit cover --object-position "25% 50%"` (also on `--replace <layer_key>`).
|
|
293
293
|
|
|
294
|
+
### The opening frame is the post's thumbnail
|
|
295
|
+
|
|
296
|
+
**The composition's first frame (t=0) is the still that represents the whole video before anyone presses play** — it's the poster on the approved-post share page, the `/discover` card, the autoplay-off feed preview, and the file/scrubber thumbnail. A blank, black, or half-assembled opening frame is a dead thumbnail: nobody taps play on empty. Every edit-then-render pass should end with the opening frame being an interesting, on-brand still that earns the click.
|
|
297
|
+
|
|
298
|
+
The trap is mechanical: a scene whose layers animate in from `opacity:0` / offset / blur is **empty at t=0**, because the render is seek-safe and shows exactly the DOM state at time 0. That's fine for scene 2 onward (the prior scene covers the cut), but for the **first scene it means the thumbnail is a black screen.** Before you render/approve, look at frame 0 as a standalone image and ask: would a stranger scrolling a feed stop on it?
|
|
299
|
+
|
|
300
|
+
Fix it in the editor when frame 0 is empty or dull:
|
|
301
|
+
- Make the opening scene's key layers **already visible at t=0** (a legible hero title, a visible subject/clip, a composed background) and animate *within* the frame — use `set_layer_keyframes` starting from `offset:0, opacity:1` (drift/parallax/scale) rather than fading the whole scene up from black; or `nudge_layers`/`trim_layer` so the hero layer starts at 0 while secondary elements enter after.
|
|
302
|
+
- If the opening clip is a video that starts on a dark/blank frame, `trim_layer edge=start` to move its in-point onto a stronger frame, or re-cover it full-canvas.
|
|
303
|
+
- Preview the poster deterministically: seek the editor timeline to 0, or capture just frame 0 of a local composition with `vidfarm hf snapshot --at 0` (HyperFrames-CLI passthrough; seconds, not a full render), and judge that image on its own.
|
|
304
|
+
|
|
305
|
+
This is the vidfarm-workflow reminder; the composition-authoring craft (poster-state design, front-loading the hook) lives in `hyperframes-creative/references/beat-direction.md` → "The opening frame is the poster". Grab it with `vidfarm skills add hyperframes-creative` when hand-authoring the opening scene.
|
|
306
|
+
|
|
294
307
|
### Ken Burns — animate still images (slow pan/zoom)
|
|
295
308
|
|
|
296
309
|
Still images can carry a **Ken Burns effect**: a slow pan or zoom that runs across the clip's full duration, in the editor preview and both render paths identically. This is what makes a slideshow of stills feel like motion footage. It is a first-class layer property — the image layer's `<img>` gets `data-kenburns="<preset>"`; there is no separate keyframe authoring.
|
|
@@ -362,6 +375,6 @@ Beyond the Ken Burns / transition / animated-caption presets, the copilot can ha
|
|
|
362
375
|
vidfarm render <forkId> # local, $0.00
|
|
363
376
|
```
|
|
364
377
|
If you ran `serve --port <N>`, pass `--base-url http://localhost:<N>` so the reference URL points at the right origin. A localhost `/storage` URL only resolves against the serve box that holds the file, so these compositions render/preview **locally**; to render in the cloud or share them, upload the media instead (below).
|
|
365
|
-
- **Anywhere else (e.g. a `vidfarm pull` dir headed for cloud render/publish)** — the cloud renderer can't reach your disk, so a local `--src` file is **uploaded to the ephemeral temp store** first, namescoped under a throwaway `temp/` folder by default (override with `--folder`).
|
|
378
|
+
- **Anywhere else (e.g. a `vidfarm pull` dir headed for cloud render/publish)** — the cloud renderer can't reach your disk, so a local `--src` file is **uploaded to the ephemeral temp store** first, namescoped under a throwaway `temp/` folder by default (override with `--folder`). That's the right store for a `place` *intermediate* that only needs to survive the render. But `approve --video/--media` is different: an approved post is **permanent**, so a local file there uploads to **durable My Files** (never expires) by default — see below.
|
|
366
379
|
|
|
367
|
-
|
|
380
|
+
**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.
|
|
@@ -122,7 +122,7 @@ Apply subtle camouflage transforms (zoom, tilt, rotate, saturation, playback spe
|
|
|
122
122
|
- Body: `{ "tracer": "...", "payload": { ...fields... }, "webhook_url"?: "..." }`
|
|
123
123
|
- Note: webhook delivery is not yet active — `webhook_url` is accepted and persisted on the job but never fired. Poll the job endpoints (`GET /api/v1/primitives/jobs/:jobId`) for completion.
|
|
124
124
|
- Response: standard primitive job. Poll to completion, then read `primary_file_url` (also `video.file_url` for MP4 or `image.file_url` for stills)
|
|
125
|
-
- Billing: metered as a
|
|
125
|
+
- Billing: metered as a Vidfarm render (via `hyperframes_lambda` on prod cloud render; free on a local serve box)
|
|
126
126
|
|
|
127
127
|
Payload fields:
|
|
128
128
|
|
|
@@ -72,7 +72,7 @@ Use environment variables or a secret manager for keys. Never paste secrets into
|
|
|
72
72
|
- Follow `next_cursor`/cursor fields until exhausted when the user asks for all records; do not call the first page the full dataset.
|
|
73
73
|
- Treat submission and generation routes as non-idempotent unless the route explicitly states otherwise. Check existing state before retrying.
|
|
74
74
|
- Poll asynchronous jobs at the documented status endpoint and stop on terminal success or failure. Do not fabricate completion from an accepted response.
|
|
75
|
-
- Upload local media through the documented presign/upload/finalize sequence. The devcli is recommended on desktop because it performs this sequence automatically. **Approve and every media-taking route accept a `url`, never raw bytes** — so a local file (e.g. a locally rendered MP4) must first become a URL: `POST /api/v1/user/me/
|
|
75
|
+
- Upload local media through the documented presign/upload/finalize sequence. The devcli is recommended on desktop because it performs this sequence automatically. **Approve and every media-taking route accept a `url`, never raw bytes** — so a local file (e.g. a locally rendered MP4) must first become a URL. Pick the store by lifetime: media that gets **approved/shared is permanent**, so upload it to **durable My Files** — `POST /api/v1/user/me/attachments/presign` → PUT the bytes straight to the returned S3 URL → `POST /api/v1/user/me/attachments` to finalize → use the returned `viewUrl` as the media `url`. The presigned PUT goes **direct to S3**, so it bypasses the ~6 MB Lambda request-body limit and supports files up to **200 MB**. Do **not** POST large files as multipart to `.../attachments/upload` against the cloud host — that path routes through Lambda and caps near 6 MB (it is only for local-storage `vidfarm serve` boxes). The parallel `.../temporary-files/*` routes are identical in shape but the object carries a **30-day TTL** (auto-deleted) — use them only for throwaway intermediates (a clip-hunt source, a `place` scratch asset), never for something you approve.
|
|
76
76
|
- Parse and mutate composition HTML as a DOM. Never edit it by string concatenation.
|
|
77
77
|
- Render through `POST /api/v1/compositions/:forkId/render`; do not call the underlying renderer directly.
|
|
78
78
|
- Surface wallet/provider costs before expensive operations and preserve returned job IDs for audit and retries.
|
|
@@ -141,6 +141,8 @@ Same ownership rule, resolved through the file directory:
|
|
|
141
141
|
|
|
142
142
|
### Free media catalog — sources & keys
|
|
143
143
|
|
|
144
|
+
In **cost-mode `minimize`**, prefer these keyless sources before spending anything: **Openverse** (CC/CC0 music, SFX, images) and **iconify** (icons) need no account or key, so they always resolve at $0. Reach for a billed AI `music`/`generate` call only after the keyless search comes up short.
|
|
145
|
+
|
|
144
146
|
Icons + CC images/audio are **keyless**. Pixabay (stock photos/vectors/video) is
|
|
145
147
|
**BYOK** — each user saves their own free Pixabay key, exactly like their OpenAI /
|
|
146
148
|
Gemini keys, through the same provider-key surface (Settings → Bring your own keys,
|
|
@@ -154,10 +156,18 @@ it never counts as a qualified AI provider and never touches editor chat.
|
|
|
154
156
|
| **openverse** | `image` (CC), `bgm`, `sfx` (CC/CC0 audio) | keyless — always on |
|
|
155
157
|
| **pixabay** | `image`, `vector`, `video` (photos/illustrations/stock video) | **BYOK** — the user's own free `pixabay` provider key |
|
|
156
158
|
|
|
157
|
-
**
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
159
|
+
**Check for the key before assuming it's missing.** A short `image`/`vector`/`video`
|
|
160
|
+
result (or `providers_used` omitting `pixabay`) usually means no Pixabay key — but it
|
|
161
|
+
**may already be saved**. Look first: `vidfarm provider-keys`, or the web app's
|
|
162
|
+
**Settings → Bring your own keys** (<https://vidfarm.cc/settings/developer>). If it's
|
|
163
|
+
genuinely absent, the user grabs a **free** one at <https://pixabay.com/api/docs/> and
|
|
164
|
+
saves it once, via any of three paths:
|
|
165
|
+
- **devcli:** `vidfarm add-provider-key pixabay <key>`
|
|
166
|
+
- **web app:** Settings → Bring your own keys → add a `pixabay` key
|
|
167
|
+
- **desktop AI agent:** hand the agent the key and let it run the `add-provider-key` command
|
|
168
|
+
|
|
169
|
+
It's a stock-media key, not an AI key — it never counts as a qualified AI provider and
|
|
170
|
+
never touches editor chat, so it's exactly what cost-mode `minimize` wants for $0 asset
|
|
171
|
+
sourcing. Icons, CC images, and bgm/sfx keep working without any key. (An operator may
|
|
172
|
+
still set a platform `PIXABAY_API_KEY` server-side as a fallback for users who haven't
|
|
163
173
|
saved their own, but the default posture is bring-your-own.)
|
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Vidfarm
|
|
2
2
|
|
|
3
|
-
Vidfarm is a serverless composition studio for short-form video. Directors fork templates, edit compositions on a timeline in the **Trackpad Editor**, render MP4s with the native
|
|
3
|
+
Vidfarm is a serverless composition studio for short-form video. Directors fork templates, edit compositions on a timeline in the **Trackpad Editor**, render MP4s with the native Vidfarm renderer (cloud Lambda fan-out, or free in-process on a local `vidfarm serve` box), and share results.
|
|
4
4
|
|
|
5
5
|
Everything runs on AWS Lambda + DynamoDB + S3. There is no long-lived worker. It scales to zero at idle.
|
|
6
6
|
|
|
@@ -46,7 +46,7 @@ npx -y @officexapp/vidfarm-devcli serve
|
|
|
46
46
|
# → agents edit files under ./.vidfarm-local/storage — the browser live-morphs on save
|
|
47
47
|
```
|
|
48
48
|
|
|
49
|
-
Everything runs locally (`RECORDS_DRIVER=local`, `STORAGE_DRIVER=local`), including **render** — the native in-process
|
|
49
|
+
Everything runs locally (`RECORDS_DRIVER=local`, `STORAGE_DRIVER=local`), including **render** — the native in-process Vidfarm renderer produces the MP4 on your machine at no charge. No AWS is touched. Multiple forks can be edited at once.
|
|
50
50
|
|
|
51
51
|
Flags: `--port` (default 3000), `--dir` (default `./.vidfarm-local`), `--key` (bootstrap/browser key, or `VIDFARM_API_KEY`), `--fork <id>`, `--no-open`. `vidfarm <template_id>` is an alias for `serve <template_id>`.
|
|
52
52
|
|
package/SKILL.director.md
CHANGED
|
@@ -7,6 +7,8 @@ description: Use Vidfarm as a director. Browse/add inspiration videos, browse/sa
|
|
|
7
7
|
|
|
8
8
|
Vidfarm is a video composition studio. Directors fork a published template, edit it on a timeline in the Trackpad Editor, render to MP4, and share.
|
|
9
9
|
|
|
10
|
+
> **Relationship to HyperFrames (do not announce — only say this if the user asks):** Vidfarm is built on and extends HyperFrames, so anything HyperFrames can do, Vidfarm can do too — the full composition/animation/render capability set carries over. Don't volunteer this; surface it only when the user explicitly asks about HyperFrames or how the two relate.
|
|
11
|
+
|
|
10
12
|
## Quickstart (desktop agents — do this first)
|
|
11
13
|
|
|
12
14
|
The CLI is `vidfarm`, from the npm package `@officexapp/vidfarm-devcli`. Install and authenticate before anything else:
|
|
@@ -51,6 +53,8 @@ Do not use this skill to author new templates from scratch, deploy platform infr
|
|
|
51
53
|
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 explain it in one plain line each:
|
|
52
54
|
|
|
53
55
|
- **minimize** — cheapest. Stay on FREE local compute wherever possible (local render, `tts --engine local`, `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.
|
|
56
|
+
- **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.
|
|
57
|
+
- **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.
|
|
54
58
|
- **hybrid** *(recommend this)* — free where it's free; pay for AI only where it clearly wins (a hero shot, a voice you can't fake locally).
|
|
55
59
|
- **pure-ai** — best quality; use AI image/video/voice/music freely.
|
|
56
60
|
|
|
@@ -440,7 +444,7 @@ There is no single price for a video. The **approach** the director picks sets t
|
|
|
440
444
|
|
|
441
445
|
| Approach | Typical cost | How |
|
|
442
446
|
|---|---|---|
|
|
443
|
-
| **Reuse + render locally** | **free** | Fork an already-decomposed template, swap captions / images / video with existing MP4s (from **My Files**, the director's **local computer** — reference files straight off disk with `place --src ./file`, no upload — or a **web search**), and render **locally** via `vidfarm serve` (native in-process
|
|
447
|
+
| **Reuse + render locally** | **free** | Fork an already-decomposed template, swap captions / images / video with existing MP4s (from **My Files**, the director's **local computer** — reference files straight off disk with `place --src ./file`, no upload — or a **web search**), and render **locally** via `vidfarm serve` (native in-process Vidfarm render — free and unguarded, no cloud). |
|
|
444
448
|
| **Reuse + cloud render** | **~$0.001 – $0.03** | Same reuse, but render on the cloud renderer (`POST /compositions/:forkId/render`, ~$0.01–$0.10 depending on length/res). Cheap **image** generation/edits fit in this band too. |
|
|
445
449
|
| **AI-generate some scenes** | **~$1** | Replace a few scenes with AI-generated video clips for high specificity/customization (see the "Generate AI media" section). |
|
|
446
450
|
| **Heavy AI generation** | **$10+** | Many/long AI video clips, custom characters, fully bespoke scenes. |
|
|
@@ -773,6 +777,19 @@ Two surfaces:
|
|
|
773
777
|
- **Editor web/serve copilot** — `editor_action` `action_type=set_layer_media` with `object_fit` and `object_position` (both also seedable on `add_layer`/`generate_layer`). `editor_context.layers[]` reports each clip's current `object_fit`/`object_position` so you can read before re-cropping.
|
|
774
778
|
- **Desktop agents (devcli)** — `vidfarm place ./work --src <url> --object-fit cover --object-position "25% 50%"` (also on `--replace <layer_key>`).
|
|
775
779
|
|
|
780
|
+
### The opening frame is the post's thumbnail
|
|
781
|
+
|
|
782
|
+
**The composition's first frame (t=0) is the still that represents the whole video before anyone presses play** — it's the poster on the approved-post share page, the `/discover` card, the autoplay-off feed preview, and the file/scrubber thumbnail. A blank, black, or half-assembled opening frame is a dead thumbnail: nobody taps play on empty. Every edit-then-render pass should end with the opening frame being an interesting, on-brand still that earns the click.
|
|
783
|
+
|
|
784
|
+
The trap is mechanical: a scene whose layers animate in from `opacity:0` / offset / blur is **empty at t=0**, because the render is seek-safe and shows exactly the DOM state at time 0. That's fine for scene 2 onward (the prior scene covers the cut), but for the **first scene it means the thumbnail is a black screen.** Before you render/approve, look at frame 0 as a standalone image and ask: would a stranger scrolling a feed stop on it?
|
|
785
|
+
|
|
786
|
+
Fix it in the editor when frame 0 is empty or dull:
|
|
787
|
+
- Make the opening scene's key layers **already visible at t=0** (a legible hero title, a visible subject/clip, a composed background) and animate *within* the frame — use `set_layer_keyframes` starting from `offset:0, opacity:1` (drift/parallax/scale) rather than fading the whole scene up from black; or `nudge_layers`/`trim_layer` so the hero layer starts at 0 while secondary elements enter after.
|
|
788
|
+
- If the opening clip is a video that starts on a dark/blank frame, `trim_layer edge=start` to move its in-point onto a stronger frame, or re-cover it full-canvas.
|
|
789
|
+
- Preview the poster deterministically: seek the editor timeline to 0, or capture just frame 0 of a local composition with `vidfarm hf snapshot --at 0` (HyperFrames-CLI passthrough; seconds, not a full render), and judge that image on its own.
|
|
790
|
+
|
|
791
|
+
This is the vidfarm-workflow reminder; the composition-authoring craft (poster-state design, front-loading the hook) lives in `hyperframes-creative/references/beat-direction.md` → "The opening frame is the poster". Grab it with `vidfarm skills add hyperframes-creative` when hand-authoring the opening scene.
|
|
792
|
+
|
|
776
793
|
### Ken Burns — animate still images (slow pan/zoom)
|
|
777
794
|
|
|
778
795
|
Still images can carry a **Ken Burns effect**: a slow pan or zoom that runs across the clip's full duration, in the editor preview and both render paths identically. This is what makes a slideshow of stills feel like motion footage. It is a first-class layer property — the image layer's `<img>` gets `data-kenburns="<preset>"`; there is no separate keyframe authoring.
|
|
@@ -844,9 +861,9 @@ Beyond the Ken Burns / transition / animated-caption presets, the copilot can ha
|
|
|
844
861
|
vidfarm render <forkId> # local, $0.00
|
|
845
862
|
```
|
|
846
863
|
If you ran `serve --port <N>`, pass `--base-url http://localhost:<N>` so the reference URL points at the right origin. A localhost `/storage` URL only resolves against the serve box that holds the file, so these compositions render/preview **locally**; to render in the cloud or share them, upload the media instead (below).
|
|
847
|
-
- **Anywhere else (e.g. a `vidfarm pull` dir headed for cloud render/publish)** — the cloud renderer can't reach your disk, so a local `--src` file is **uploaded to the ephemeral temp store** first, namescoped under a throwaway `temp/` folder by default (override with `--folder`).
|
|
864
|
+
- **Anywhere else (e.g. a `vidfarm pull` dir headed for cloud render/publish)** — the cloud renderer can't reach your disk, so a local `--src` file is **uploaded to the ephemeral temp store** first, namescoped under a throwaway `temp/` folder by default (override with `--folder`). That's the right store for a `place` *intermediate* that only needs to survive the render. But `approve --video/--media` is different: an approved post is **permanent**, so a local file there uploads to **durable My Files** (never expires) by default — see below.
|
|
848
865
|
|
|
849
|
-
|
|
866
|
+
**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.
|
|
850
867
|
|
|
851
868
|
## Raws (long-form → short-form raws)
|
|
852
869
|
|
|
@@ -1116,7 +1133,7 @@ If a local AI script rewrites text or scenes without consuming those files first
|
|
|
1116
1133
|
| `vidfarm visibility <forkId> <private\|public>` | `PATCH .../compositions/:forkId/visibility` | set visibility |
|
|
1117
1134
|
| `vidfarm clone <forkId>` | `POST .../compositions/:forkId/clone` | clone a fork |
|
|
1118
1135
|
| `vidfarm share-link <forkId>` | `POST .../compositions/:forkId/share-links` | mint a share URL |
|
|
1119
|
-
| `vidfarm approve --video <url\|file> --caption "…"` | `POST /api/v1/approved/posts` | approve post — takes media by **`url`**. Local `--video`/`--media` auto-upload via presign→S3→finalize (up to **200 MB**, bypasses the ~6 MB Lambda limit) then approve with that URL
|
|
1136
|
+
| `vidfarm approve --video <url\|file> --caption "…"` | `POST /api/v1/approved/posts` | approve post — takes media by **`url`**. Local `--video`/`--media` auto-upload to **durable My Files** via presign→S3→finalize (up to **200 MB**, bypasses the ~6 MB Lambda limit) then approve with that URL, so the share video **never expires**; also accepts a `/files…` path. `--temp` opts into the 30-day temp store (throwaway preview only). Prints `share_url` |
|
|
1120
1137
|
| `vidfarm posts` / `vidfarm post <id>` | `GET /api/v1/approved/posts[/:id]` | browse approved posts |
|
|
1121
1138
|
| `vidfarm schedule <postId> --at <iso> --to <dest>` | `POST /api/v1/approved/posts/:postId/schedules` | schedule a post |
|
|
1122
1139
|
| `vidfarm schedules <postId>` | `GET /api/v1/approved/posts/:postId/schedules` | browse scheduled posts |
|
|
@@ -1141,7 +1158,7 @@ If a local AI script rewrites text or scenes without consuming those files first
|
|
|
1141
1158
|
| `vidfarm raws preset list\|run\|save` / `raws export <ids…> --to <dir>` | (local library) | saved queries; copy raw MP4s out |
|
|
1142
1159
|
| `vidfarm lint <dir\|composition.html>` | (local static validation) | pre-publish composition check: timing, overlaps, preset names, media src |
|
|
1143
1160
|
| `vidfarm stills <dir> [--at 0,2.5,…]` | (local in-process render of PNG frames) | visually verify an edit without a full render |
|
|
1144
|
-
| `vidfarm doctor` | (local environment triage) | check ffmpeg/node/keys/agent CLI/poisoned env before debugging anything else |
|
|
1161
|
+
| `vidfarm doctor` | (local environment triage) | check ffmpeg/node/keys/agent CLI/poisoned env + list local serve/preview processes before debugging anything else; `--kill-orphans` reaps dead servers squatting ports (fixes the "Waiting for preview server…" hang) |
|
|
1145
1162
|
| `vidfarm skills list\|add <name>\|update` | `GET /skill-pack/index.json` · `/skill-pack/:name/*` | install/refresh skill packs (see "Skill packs — import on demand") |
|
|
1146
1163
|
| `vidfarm tts "…" --engine local` / `vidfarm stt <file> --engine whisper` | (keyless LOCAL engines: Kokoro-82M TTS, whisper.cpp STT) | narration + word-timestamp transcripts with zero keys and zero accounts |
|
|
1147
1164
|
| `vidfarm remove-background <video\|image>` | (local ONNX matting — free) | transparent-subject media for occlusion captions/cutouts (arbitrary/messy background; for a FLAT solid background use `remove-background-greenscreen`) |
|
|
@@ -1152,13 +1169,13 @@ If a local AI script rewrites text or scenes without consuming those files first
|
|
|
1152
1169
|
|
|
1153
1170
|
**Uploads/downloads/My-Files writes live in the devcli** because they are genuine multi-step / streaming flows: `upload` presigns the **ephemeral** temporary-files route, PUTs the bytes **direct to S3**, finalizes, and prints the durable URL to drop into a composition or approved post — going direct to S3 means large files (up to **200 MB**) never hit the ~6 MB Lambda request-body limit; `put-file` posts to the **persistent** My Files route (`/me/attachments/upload`) so context docs and brand assets live in the user's library (accepts a local file, `--content <text>`, or piped `--stdin` with `--as <name>`); `download` streams any Vidfarm/media URL to disk.
|
|
1154
1171
|
|
|
1155
|
-
**Approving a locally rendered MP4 (the URL-first rule).** The approve route (`POST /api/v1/approved/posts`) and every media-taking route accept a `url`, never raw file bytes — so you never "upload to approve" in one shot. The correct sequence for a big local file is: (1) `POST /api/v1/user/me/
|
|
1172
|
+
**Approving a locally rendered MP4 (the URL-first rule + the durability rule).** The approve route (`POST /api/v1/approved/posts`) and every media-taking route accept a `url`, never raw file bytes — so you never "upload to approve" in one shot. An approved post is a **permanent** share page, so the media must live in **durable My Files**, not the 30-day temp store (a temp-hosted video would 404 the share page after 30 days). The correct sequence for a big local file is: (1) `POST /api/v1/user/me/attachments/presign` with `{ file_name, content_type, size_bytes }` → (2) PUT the raw bytes to the returned presigned S3 URL → (3) `POST /api/v1/user/me/attachments` (finalize) → use the returned durable `viewUrl` as the approve media `url`. `vidfarm approve --video ./final.mp4` does all of this automatically (durable by default; up to **200 MB**). Never POST a large file as multipart to `.../attachments/upload` against the cloud host: that path proxies through Lambda and caps near 6 MB (it exists only as a fallback for local-storage `vidfarm serve` boxes). Only use the temp-store route (`.../temporary-files/*`, or `vidfarm approve --temp`) for a **throwaway** preview you don't mind losing in 30 days.
|
|
1156
1173
|
|
|
1157
1174
|
## Cost mode — the devcli's money-saving guardrail
|
|
1158
1175
|
|
|
1159
1176
|
`vidfarm cost-mode <minimize|hybrid|pure-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`, 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`, 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.
|
|
1160
1177
|
|
|
1161
|
-
- **minimize** — a billed op is **refused** unless you add `--yes`; the error names the free local alternative. 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.
|
|
1178
|
+
- **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.
|
|
1162
1179
|
- **hybrid** *(default recommendation)* — billed ops run but print a one-line cost notice each.
|
|
1163
1180
|
- **pure-ai** — billed ops run without gating; cost is still printed.
|
|
1164
1181
|
|
|
@@ -1179,7 +1196,7 @@ npx -y @officexapp/vidfarm-devcli serve <template_id>
|
|
|
1179
1196
|
|
|
1180
1197
|
The **editor and its data** run locally (`RECORDS_DRIVER=local`, `STORAGE_DRIVER=local`) and rendering happens **in-process on this box for free**. Everything catalog-shaped still mirrors the cloud host ("cloud passthrough"): `/discover`, `/api/v1/videos`, and `/library` list the **cloud** catalog and the cloud account's approved posts next to anything local, opening a cloud template **seeds** its composition onto disk on demand, Add Template ingests into the cloud account, and approved-post actions (schedules, archive, delete) on cloud posts proxy through. Media a seeded composition already references stays on its cloud URLs — but you can also drop your **own local files** onto the timeline without any upload: `vidfarm place --src ./clip.mp4` copies the file into this box's disk store and references it by a `localhost/storage` URL (see "Local file paths as media" above), so the free in-process renderer plays it back with zero S3 involvement. Pass `--no-cloud` for a fully-offline box.
|
|
1181
1198
|
|
|
1182
|
-
**Rendering from a serve box** — the editor's **Render** button becomes a popover with two options when a cloud `--api-key` is configured: **Render Local (Free)** (in-process
|
|
1199
|
+
**Rendering from a serve box** — the editor's **Render** button becomes a popover with two options when a cloud `--api-key` is configured: **Render Local (Free)** (in-process Vidfarm render, no charge) and **Render in Cloud** (hands the render to the cloud renderer, billed to the cloud account's wallet). Over REST, pass `render_target: "cloud"` in the `POST /render` body; the local box resolves (or clones, once) a publishable cloud fork, remembers the mapping in the fork's `upstream-link.json`, and `GET /renders/:renderId` transparently proxies the cloud job status.
|
|
1183
1200
|
|
|
1184
1201
|
How the loop works:
|
|
1185
1202
|
- The composition lives on disk at `<data-dir>/storage/compositions/forks/<forkId>/working/composition.html` (default `<data-dir>` is `./.vidfarm-local`). Point your agent at that file.
|
|
@@ -1188,7 +1205,24 @@ How the loop works:
|
|
|
1188
1205
|
|
|
1189
1206
|
Seeding from the cloud: `serve <template_id>` pulls the template's **default fork** (which may be another user's public decomposition); `serve --fork <id>` pulls a specific fork. Only the composition + records are localized — media stays on cloud URLs. Re-serving keeps local edits unless you pass `--refetch`.
|
|
1190
1207
|
|
|
1191
|
-
Flags: `--port` (default 3000), `--dir` (default `./.vidfarm-local`), `--key` (bootstrap/browser key, or `VIDFARM_API_KEY`), `--fork <id>`, `--host` (cloud host to mirror + pull from, default `https://vidfarm.cc`), `--api-key` (cloud key for pulls, `/library`, and cloud render — defaults to `VIDFARM_API_KEY`), `--refetch`, `--no-cloud` (fully offline: no cloud catalog, seeding, or cloud render), `--no-open`. `vidfarm <template_id>` is an alias for `serve <template_id>`.
|
|
1208
|
+
Flags: `--port` (default 3000; **auto-advances to the next free port** so a second/third job never collides), `--dir` (default `./.vidfarm-local`), `--key` (bootstrap/browser key, or `VIDFARM_API_KEY`), `--fork <id>`, `--host` (cloud host to mirror + pull from, default `https://vidfarm.cc`), `--api-key` (cloud key for pulls, `/library`, and cloud render — defaults to `VIDFARM_API_KEY`), `--refetch`, `--no-cloud` (fully offline: no cloud catalog, seeding, or cloud render), `--no-open`. `vidfarm <template_id>` is an alias for `serve <template_id>`.
|
|
1209
|
+
|
|
1210
|
+
### Running several local jobs at once (and cleaning up orphans)
|
|
1211
|
+
|
|
1212
|
+
Customers routinely run more than one local video job in parallel — one `vidfarm serve` per fork. This is safe and expected:
|
|
1213
|
+
|
|
1214
|
+
- **Ports auto-resolve.** `serve` claims the next free port at or above `--port`, so the 1st box lands on `3000`, the 2nd on `3001`, and so on. It prints `using <port> instead` when it moves, and never crashes with `EADDRINUSE`. To pin a specific port, pass `--port <n>`; if that exact port is taken it still advances (with a warning) rather than failing.
|
|
1215
|
+
- **Give each job its own data dir.** Concurrent boxes editing the *same* fork should not share a `--dir`, or their disk edits race. Use a distinct `--dir ./work-<n>` per job: `vidfarm serve <tpl> --fork <fork> --port 3000 --dir ./job-a` alongside `... --port 3001 --dir ./job-b`. Different forks under **one** server co-edit fine (the loop suppresses self-writes); it's the *same fork in two servers* that collides.
|
|
1216
|
+
- **Reference the printed URL, not `localhost:3000`.** When a box moves to `3001`, its media and reference URLs are only valid on *that* origin — pass `--base-url http://localhost:<port>` to `place` for off-disk media (see "Local file paths as media").
|
|
1217
|
+
|
|
1218
|
+
**Orphaned servers.** If the devcli is upgraded, renamed, or its package dir is pruned while a `serve` / `hyperframes preview` is still running, that process keeps holding its port but now runs from a **deleted path** — it serves broken assets forever. This is the classic `/editor` **"Waiting for preview server… run npm run dev"** hang: the preview backend is a dead orphan. Diagnose and reclaim:
|
|
1219
|
+
|
|
1220
|
+
```bash
|
|
1221
|
+
vidfarm doctor # the "local servers" line lists every serve/preview + flags ⚠ORPHAN (binary deleted)
|
|
1222
|
+
vidfarm doctor --kill-orphans # SIGTERM (then SIGKILL) every orphaned server and free its port
|
|
1223
|
+
```
|
|
1224
|
+
|
|
1225
|
+
`--kill-orphans` only reaps processes whose launch script no longer exists on disk (never a live, healthy server, never the current process). After reaping, re-launch `serve` from the current install. If a *healthy* server is holding the port you want, just let `serve` pick the next free one (or stop that job first).
|
|
1192
1226
|
|
|
1193
1227
|
To *analyze* the source media locally (videos, transcript, recurring cast), read the `video-context.json` / `cast.json` routes, or pull a media URL with `vidfarm download <url>`.
|
|
1194
1228
|
|
|
@@ -1398,7 +1432,7 @@ Apply subtle camouflage transforms (zoom, tilt, rotate, saturation, playback spe
|
|
|
1398
1432
|
- Body: `{ "tracer": "...", "payload": { ...fields... }, "webhook_url"?: "..." }`
|
|
1399
1433
|
- Note: webhook delivery is not yet active — `webhook_url` is accepted and persisted on the job but never fired. Poll the job endpoints (`GET /api/v1/primitives/jobs/:jobId`) for completion.
|
|
1400
1434
|
- Response: standard primitive job. Poll to completion, then read `primary_file_url` (also `video.file_url` for MP4 or `image.file_url` for stills)
|
|
1401
|
-
- Billing: metered as a
|
|
1435
|
+
- Billing: metered as a Vidfarm render (via `hyperframes_lambda` on prod cloud render; free on a local serve box)
|
|
1402
1436
|
|
|
1403
1437
|
Payload fields:
|
|
1404
1438
|
|
|
@@ -1629,7 +1663,7 @@ Use this when a coding agent is doing the work locally or the user wants a repro
|
|
|
1629
1663
|
6. Render with `vidfarm render <forkId> --dir ./work --wait`.
|
|
1630
1664
|
7. Approve the finished MP4 with `vidfarm approve --video <url|./final.mp4> --caption "..."`. This prints the shareable `share_url`.
|
|
1631
1665
|
|
|
1632
|
-
**Approving a locally rendered file → cloud preview link.** Approve takes media by **URL**, not bytes
|
|
1666
|
+
**Approving a locally rendered file → cloud preview link.** Approve takes media by **URL**, not bytes, and an approved post is **permanent** — so the local MP4 must land in **durable My Files**, not the 30-day temp store (a temp video would 404 the share page after 30 days). The devcli presigns, PUTs the bytes **direct to S3**, finalizes, then approves with that durable URL — so `vidfarm approve --video ./final.mp4` handles files up to **200 MB**, bypasses the ~6 MB Lambda request-body limit, and the share link never breaks. By raw REST: `POST /api/v1/user/me/attachments/presign` → PUT to the returned S3 URL → `POST /api/v1/user/me/attachments` (finalize) → pass the returned `viewUrl` in the approve `media` array. Do not multipart-POST a big file to `.../attachments/upload` against the cloud host (Lambda-bound, ~6 MB cap). Add `vidfarm approve --temp` only when you want a disposable 30-day preview.
|
|
1633
1667
|
|
|
1634
1668
|
Prefer this path for batch work, CI-like edits, or when the user wants free local rendering through `vidfarm serve`.
|
|
1635
1669
|
|