@mevdragon/vidfarm-devcli 0.20.1 → 0.20.3
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/editor-capabilities/SKILL.md +2 -1
- package/SKILL.director.md +96 -10
- package/SKILL.platform.md +3 -3
- package/dist/src/app.js +326 -24
- package/dist/src/cli.js +253 -26
- package/dist/src/devcli/sync.js +66 -9
- package/dist/src/editor-chat.js +2 -2
- package/dist/src/services/hyperframes.js +162 -28
- package/package.json +1 -1
|
@@ -142,9 +142,10 @@ You usually already know a clip's aspect from how you sourced it (a hunted raw's
|
|
|
142
142
|
- `transitions` — `default`/`intro`/`outro` (map to one `set_transitions` call), `usage`.
|
|
143
143
|
- `broll` — `reliance`, `sourcing`, `shot_kinds`, `cadence`. Drives whether you HUNT raws (`/clips/scan`) vs `generate_layer`, and at what cadence.
|
|
144
144
|
- `audio` — `voiceover`, `music`, `sfx`, `captions_from`.
|
|
145
|
+
- `emotional` — HOW to edit so the FEELING survives: `target_feeling`, `tone`, `comedic_timing` (the beat/pause/hard-cut that sells the joke), `intonation` (vocal cadence to preserve when you re-voice), `vibe_anchors` (the concrete edit moves that carry the emotion — often "keep the trending sound and land the beat drop on the reveal cut"). The vibe/joke/intonation/sound are the first things a remix flattens, so treat these as must-dos.
|
|
145
146
|
- `scenes[]` — each beat's `role` + `importance` + `must_keep` + `edit_bias`. `important_scenes`, `editing_bias`, `do`, `dont`.
|
|
146
147
|
|
|
147
|
-
**How to use it:** map `caption_style` → `set_captions`, `transitions.*` → `set_transitions`, honor `pacing`, follow `broll` for footage sourcing, lay `audio
|
|
148
|
+
**How to use it:** map `caption_style` → `set_captions`, `transitions.*` → `set_transitions`, honor `pacing`, follow `broll` for footage sourcing, lay `audio`, and honor `emotional` (preserve `comedic_timing`, keep `intonation` when re-voicing narration, execute every `vibe_anchors` entry). **PROTECT** any beat in `important_scenes` or with `must_keep:true` / `importance:"critical"` — swap its subject if needed but preserve its timing, role, and caption cadence; that's where the style lives. Treat `editing_bias`/`do`/`dont` as hard constraints for this composition. It sets defaults, not a straitjacket — an explicit user instruction wins. **In local devcli** the same brief is on disk as `editor-harness.json` (under `.harness`, pulled by `vidfarm pull`/`sync`); read it there before a big edit. If `editor_harness` is absent, the fork wasn't decomposed with the harness pass — fall back to `composition_context` + the `video_context` tool.
|
|
148
149
|
|
|
149
150
|
## Web-editor motion rule (hard constraint)
|
|
150
151
|
|
package/SKILL.director.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: vidfarm-director
|
|
3
|
-
description: Use Vidfarm as a director. Browse/add inspiration videos, fork a template into a composition, edit it in the Trackpad Editor (timeline-based like Premiere/DaVinci), auto-decompose source video into scenes, render to MP4, approve into a shareable post, and schedule it. Includes login, provider keys, discovery, versioning, uploads/downloads, and billing. Every step is available as
|
|
3
|
+
description: Use Vidfarm as a director. Browse/add inspiration videos, fork a template into a composition, edit it in the Trackpad Editor (timeline-based like Premiere/DaVinci), auto-decompose source video into scenes, render to MP4, approve into a shareable post, and schedule it. Includes login, provider keys, discovery, versioning, uploads/downloads, and billing. Every step is available as raw REST; `vidfarm-devcli` wraps those routes and composes the file-backed scripting flows.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Vidfarm Director
|
|
@@ -136,6 +136,19 @@ Content-Type: application/json
|
|
|
136
136
|
|
|
137
137
|
Returns the same fork metadata shape as `POST /api/v1/compositions` (201). devcli: `vidfarm clone <forkId> [--from current|default] [--version N] [--title …]`. In the in-editor AI chat this is the `editor_action` `fork_composition` action (`fork_from: current|default`).
|
|
138
138
|
|
|
139
|
+
## Automate a template via REST
|
|
140
|
+
|
|
141
|
+
Templates now have the same job-backed REST pattern as primitives, so a script can run them repeatably without the editor UI:
|
|
142
|
+
|
|
143
|
+
```
|
|
144
|
+
POST /api/v1/templates/:templateId/operations/:operationName
|
|
145
|
+
Content-Type: application/json
|
|
146
|
+
|
|
147
|
+
{ "tracer": "my-run", "payload": { ... }, "webhook_url": "https://..." }
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
The route returns `202` with a `job_id`, and you can poll the job with `GET /api/v1/user/me/jobs/:jobId` or the template-scoped `GET /api/v1/templates/:templateId/jobs/:jobId`. devcli wraps the same flow as `vidfarm template run <templateId> <operationName> --payload-file payload.json --wait`.
|
|
151
|
+
|
|
139
152
|
## Agentic editing: the three axes (SWAP ↔ REPLACE)
|
|
140
153
|
|
|
141
154
|
Almost every editor session is a director taking a template / fork / project and **re-working** it, and a re-work only ever touches three independent axes — **SCENES** (the video/image clips carrying the visuals), **AUDIO** (narration/voiceover, music, SFX), and **TEXT** (captions, titles, overlays). On each axis the intent sits on a **SWAP ↔ REPLACE** spectrum, and the three axes in one request can sit at different points — decide each before you act:
|
|
@@ -187,7 +200,7 @@ Content-Type: application/json
|
|
|
187
200
|
|
|
188
201
|
Two modes:
|
|
189
202
|
|
|
190
|
-
- **`smart`** — uses the caller's Gemini/OpenAI/OpenRouter key. Samples frames, extracts scenes with labels, literal visual **descriptions**, and viral notes, extracts on-screen captions with positions, **transcribes the audio track verbatim** (timestamped segments; Gemini first, OpenAI Whisper fallback; skipped when the video has no audio), and detects viral DNA (hook, retention, payoff). Takes 30-60 seconds. Auto-fires GhostCut for subtitle removal in the background.
|
|
203
|
+
- **`smart`** — uses the caller's Gemini/OpenAI/OpenRouter key. Samples frames, extracts scenes with labels, literal visual **descriptions**, and viral notes, extracts on-screen captions with positions, **transcribes the audio track verbatim** (timestamped segments; Gemini first, OpenAI Whisper fallback; skipped when the video has no audio), and detects viral DNA (hook, retention, payoff, plus **`emotional_punch`** — the vibe/joke/intonation that makes it FEEL, reasoned over BOTH the frames and the transcript so the trending sound and vocal delivery are captured, not just the visuals). Takes 30-60 seconds. Auto-fires GhostCut for subtitle removal in the background.
|
|
191
204
|
- **`time-slice`** — deterministic equal-duration split. Instant. No AI provider needed.
|
|
192
205
|
|
|
193
206
|
**Source length cap.** Vidfarm caps auto-decompose (and all inspiration ingest) at **120 seconds** of source video. If the source is longer, the API responds with `400 { ok: false, code: "source_too_long", duration_seconds, max_duration_seconds }` and no scenes are written. Trim the source before forking / ingesting.
|
|
@@ -226,12 +239,25 @@ Read-only state (does **not** advance the job or bill): `GET /api/v1/composition
|
|
|
226
239
|
"viral_note": "...",
|
|
227
240
|
"transcript_excerpt": "what is spoken during this scene"
|
|
228
241
|
}],
|
|
229
|
-
"viral_dna": {
|
|
242
|
+
"viral_dna": {
|
|
243
|
+
"trend_tagline": "...", "hook": "...", "retention": "...", "payoff": "...",
|
|
244
|
+
"emotional_punch": {
|
|
245
|
+
"core_emotion": "smug relief-laugh", "tone": "deadpan comedic",
|
|
246
|
+
"arc": "flat setup → mounting absurdity → sudden turn", "peak_moment": "the reveal at ~0:07",
|
|
247
|
+
"mechanism": "subversion + trending-sound beat drop synced to the cut",
|
|
248
|
+
"humor": "the joke is X — it works because Y (rebuild the SAME joke around a new subject)",
|
|
249
|
+
"delivery": "flat VO cadence, hold the beat before the punch, beat drop lands on the reveal",
|
|
250
|
+
"preserve": ["keep the beat drop on the reveal cut", "keep the flat unbothered delivery"]
|
|
251
|
+
},
|
|
252
|
+
"preserve": ["..."], "avoid": ["..."], "promotions": ["..."], "keywords": ["..."]
|
|
253
|
+
}
|
|
230
254
|
}
|
|
231
255
|
```
|
|
232
256
|
|
|
233
257
|
`status: "none"` means the fork was never smart-decomposed — run `POST /auto-decompose` with `mode: "smart"` first. `transcript` is `null` when the source has no audio track or transcription failed.
|
|
234
258
|
|
|
259
|
+
**`viral_dna.emotional_punch`** is the FEELING of the format and what makes it land — the part a remix most often flattens. Where hook/retention/payoff are the mechanical structure, this captures the vibe, the joke, and the intonation. It is now **audio-aware**: the decompose feeds the transcript into the reasoning, so `mechanism`/`delivery` account for the trending sound, the music-bed drop, and vocal cadence (on short-form the sound is frequently the biggest driver of the punch). When you re-theme or remix, rebuild `humor`'s joke around the new subject rather than dropping it, match `tone`/`delivery` so intonation and comedic timing survive, and never trade the `peak_moment` payoff for a flat product plug.
|
|
260
|
+
|
|
235
261
|
Use it whenever you need to know what the video says or shows: writing/translating captions, matching hooks or dubs to spoken audio, or planning scene-level edits.
|
|
236
262
|
|
|
237
263
|
- **Editor chat (frontend AI)** exposes this as the optional `video_context` tool — the copilot calls it on demand.
|
|
@@ -254,13 +280,14 @@ Use it whenever you need to know what the video says or shows: writing/translati
|
|
|
254
280
|
"broll": { "reliance": "heavy", "sourcing": "screen-recording", "shot_kinds": ["b_roll"], "cadence": "new visual ~every 2s" },
|
|
255
281
|
"transitions": { "default": "cut", "intro": "fade", "outro": "none", "usage": "hard cuts only" },
|
|
256
282
|
"audio": { "voiceover": "first-person narration", "music": "upbeat lofi bed, low", "sfx": "whoosh on cuts", "captions_from": "voiceover" },
|
|
283
|
+
"emotional": { "target_feeling": "smug relief-laugh", "tone": "deadpan comedic", "comedic_timing": "hold a beat, then hard cut on the punch word", "intonation": "flat unbothered VO, no upspeak", "vibe_anchors": ["keep the trending sound, land the beat drop on the reveal cut", "hold silence before the payoff"] },
|
|
257
284
|
"scenes": [{ "role": "hook", "importance": "critical", "must_keep": true, "note": "...", "edit_bias": "swap subject, keep timing+caption cadence" }],
|
|
258
285
|
"important_scenes": ["..."], "editing_bias": ["..."], "do": ["..."], "dont": ["..."]
|
|
259
286
|
}
|
|
260
287
|
}
|
|
261
288
|
```
|
|
262
289
|
|
|
263
|
-
**Use it to pick concrete moves:** `typography.caption_style` maps to a `set_captions` preset; `transitions.*` to a `set_transitions` call; keep `pacing` (cut rhythm / avg_scene_seconds) when adding or splitting scenes; follow `broll.reliance`/`sourcing` to decide HUNT raws (`/raws/scan`) vs generate; lay `audio.*`. **Protect** any beat in `important_scenes` or with `must_keep:true` / `importance:"critical"` — swap the subject but preserve its timing, role, and caption cadence. Treat `editing_bias`/`do`/`dont` as hard constraints; an explicit user instruction still wins. `status:"none"` → run `POST /auto-decompose` first.
|
|
290
|
+
**Use it to pick concrete moves:** `typography.caption_style` maps to a `set_captions` preset; `transitions.*` to a `set_transitions` call; keep `pacing` (cut rhythm / avg_scene_seconds) when adding or splitting scenes; follow `broll.reliance`/`sourcing` to decide HUNT raws (`/raws/scan`) vs generate; lay `audio.*`. **`emotional`** is HOW to keep the FEELING while you swap the subject — the vibe/joke/intonation/sound are the first things a remix flattens, so honor `emotional.comedic_timing` (the held beat / hard cut that sells the joke), preserve `emotional.intonation` when you re-voice narration, and treat every `emotional.vibe_anchors` entry as a must-do (on short-form the trending sound / beat drop is often the biggest carrier of the punch — keep it and land it on the same cut). **Protect** any beat in `important_scenes` or with `must_keep:true` / `importance:"critical"` — swap the subject but preserve its timing, role, and caption cadence. Treat `editing_bias`/`do`/`dont` as hard constraints; an explicit user instruction still wins. `status:"none"` → run `POST /auto-decompose` first.
|
|
264
291
|
|
|
265
292
|
- **Editor chat (frontend AI)** already receives the harness inline in `editor_context.editor_harness` (no tool call needed); it can also fetch this route via `http_request`.
|
|
266
293
|
- **Desktop agents (Claude Code / Codex)**: `vidfarm pull` writes `editor-harness.json` to the fork dir (the brief is under `.harness`) alongside `video-context.json`; read it before a big edit. The `pull` grounding line reports whether it's present.
|
|
@@ -439,7 +466,7 @@ Beyond the Ken Burns / transition / animated-caption presets, the copilot can ha
|
|
|
439
466
|
|
|
440
467
|
## Render (publish to MP4)
|
|
441
468
|
|
|
442
|
-
Rendering publishes the fork's current working state to an MP4 using HyperFrames (cloud Lambda fan-out on the deployed host, or the free in-process renderer on a local `vidfarm serve` box). In the Trackpad Editor this is the **Render** button. The REST route is `/render`; the older `/export` path is kept as a deprecated alias for already-published devcli clients (treat "Render", "publish", `/render`, and `/export` as the same operation). The devcli exposes it as `vidfarm render <forkId> [--wait]`.
|
|
469
|
+
Rendering publishes the fork's current working state to an MP4 using HyperFrames (cloud Lambda fan-out on the deployed host, or the free in-process renderer on a local `vidfarm serve` box). In the Trackpad Editor this is the **Render** button. The REST route is `/render`; the older `/export` path is kept as a deprecated alias for already-published devcli clients (treat "Render", "publish", `/render`, and `/export` as the same operation). The devcli exposes it as `vidfarm render <forkId> [--wait]`; scripted local dirs can use `vidfarm render <forkId> --dir ./work --wait`.
|
|
443
470
|
|
|
444
471
|
```
|
|
445
472
|
POST /api/v1/compositions/:forkId/render
|
|
@@ -448,16 +475,18 @@ Content-Type: application/json
|
|
|
448
475
|
{ "title": "optional", "version": null, "tracer": "optional-trace-id", "html": "optional HTML to save to working state before rendering" }
|
|
449
476
|
```
|
|
450
477
|
|
|
451
|
-
Returns 202 with `{ ok, renderId, status, progress, outputUrl, cost, title, version, ... }` where `version` is the snapshot version created for this publish. Failure modes: `402` if the fork owner is not on a paid plan, `409` while GhostCut subtitle removal is still `pending` (retry after `/remove-video-captions-poll` reports done/failed), `412` if the provider-key preflight fails. Poll:
|
|
478
|
+
Returns 202 with `{ ok, renderId, status, progress, expectedOutputPublicUrl, outputUrl, cost, title, version, ... }` where `expectedOutputPublicUrl` is the deterministic public MP4 URL and `version` is the snapshot version created for this publish. Failure modes: `402` if the fork owner is not on a paid plan, `409` while GhostCut subtitle removal is still `pending` (retry after `/remove-video-captions-poll` reports done/failed), `412` if the provider-key preflight fails. Poll:
|
|
452
479
|
|
|
453
480
|
```
|
|
454
481
|
GET /api/v1/compositions/:forkId/renders/:renderId
|
|
455
482
|
```
|
|
456
483
|
|
|
457
|
-
Response includes `{ ok, renderId, status: "RUNNING" | "SUCCEEDED" | "FAILED", progress, framesRendered, totalFrames, cost, outputUrl, outputS3Uri, errors }` (`progress` is 0..1). On success, `
|
|
484
|
+
Response includes `{ ok, renderId, status: "RUNNING" | "SUCCEEDED" | "FAILED", progress, framesRendered, totalFrames, cost, expectedOutputPublicUrl, outputUrl, outputS3Uri, errors }` (`progress` is 0..1). On success, `expectedOutputPublicUrl` is the durable public MP4 URL and `outputUrl` remains the completion-time field.
|
|
458
485
|
|
|
459
486
|
Every publish creates an immutable version snapshot at `versions/<N>/composition.html` and `versions/<N>/composition.json`.
|
|
460
487
|
|
|
488
|
+
The Web UI **Render** button and devcli render both use this same endpoint. The fast `202` response includes the deterministic `expectedOutputPublicUrl` so a caller can store or pass along the final public S3 URL before the render has completed, then poll by `renderId` until `status` settles.
|
|
489
|
+
|
|
461
490
|
## Approve a finished post
|
|
462
491
|
|
|
463
492
|
A render produces a bare MP4 URL. **Approving** wraps that MP4 (plus caption, title, pinned comment, and any carousel slides) into a shareable preview page — the phone-mockup page a human opens to review and copy the post.
|
|
@@ -600,16 +629,73 @@ For agents that operate Vidfarm headlessly, the typical loop is:
|
|
|
600
629
|
3. `PATCH /api/v1/compositions/:forkId/composition.json` → update metadata
|
|
601
630
|
4. `POST /api/v1/compositions/:forkId/render { tracer }` → render
|
|
602
631
|
5. Poll `GET /api/v1/compositions/:forkId/renders/:renderId` until `SUCCEEDED`
|
|
603
|
-
6. Use `
|
|
632
|
+
6. Use `expectedOutputPublicUrl` in the next stage when you need the stable public URL immediately; otherwise fall back to `outputUrl` after completion
|
|
604
633
|
|
|
605
634
|
Send a stable `tracer` on export so retries are traceable and filterable in job history — but note that submission is **not idempotent**: every POST creates a new job (and a new charge) even with the same tracer. Do not blind-retry expensive submissions; check the render status first.
|
|
606
635
|
|
|
636
|
+
## Scripting mode
|
|
637
|
+
|
|
638
|
+
**Scripting mode** is the recommended posture for repeatable template automation. Use it when a director wants to take a template they like, agree on a base fork, and then drive bulk or one-off edits entirely through REST or `vidfarm api` from a script, Lambda, or local machine.
|
|
639
|
+
|
|
640
|
+
Contract:
|
|
641
|
+
|
|
642
|
+
1. Pick the template once, then fork once and treat the resulting `forkId` as the stable base.
|
|
643
|
+
2. Read the working state with `GET /api/v1/compositions/:forkId/composition.html` and `GET /api/v1/compositions/:forkId/composition.json`.
|
|
644
|
+
3. Modify the composition deterministically in your script.
|
|
645
|
+
4. Write back with `PUT /api/v1/compositions/:forkId/composition.html` and `PATCH /api/v1/compositions/:forkId/composition.json`.
|
|
646
|
+
5. Snapshot with `POST /api/v1/compositions/:forkId/versions` before or after render, depending on whether you want the version to capture the exact render input or the post-render state.
|
|
647
|
+
6. Render with `POST /api/v1/compositions/:forkId/render`, choosing local or cloud based on the host and runtime.
|
|
648
|
+
|
|
649
|
+
Devcli shortcut:
|
|
650
|
+
|
|
651
|
+
```bash
|
|
652
|
+
vidfarm render "$FORK_ID" --dir ./work --tracer "batch-2026-07-09-row-42" --wait
|
|
653
|
+
```
|
|
654
|
+
|
|
655
|
+
That command validates `./work/composition.html`, shallow-patches `./work/composition.json` if present, then submits the same `POST /api/v1/compositions/:forkId/render` call used by the Web UI. `--dir` may also point directly at `composition.html`.
|
|
656
|
+
|
|
657
|
+
Best practices:
|
|
658
|
+
|
|
659
|
+
- Prefer one canonical base fork per automation run, then branch from that fork if you need variants.
|
|
660
|
+
- Treat `composition.json` as a shallow merged metadata document. The server merges the posted JSON object into the existing object; it is not RFC 6902 JSON Patch.
|
|
661
|
+
- Keep edits idempotent in your script. Re-run the script against the same fork only if it computes the same desired state.
|
|
662
|
+
- Use a stable `tracer` on render jobs so logs and job history stay searchable.
|
|
663
|
+
- Use the same REST sequence in Lambda, CI, or a local shell. The only thing that changes is the host and auth.
|
|
664
|
+
|
|
665
|
+
Canonical shell shape:
|
|
666
|
+
|
|
667
|
+
```bash
|
|
668
|
+
BASE_TEMPLATE_ID="template_..."
|
|
669
|
+
FORK_ID="$(vidfarm api POST /api/v1/compositions --data "{\"template_id\":\"${BASE_TEMPLATE_ID}\"}" --json | jq -r '.fork_id')"
|
|
670
|
+
|
|
671
|
+
vidfarm api GET "/api/v1/compositions/${FORK_ID}/composition.html" --raw > /tmp/composition.html
|
|
672
|
+
vidfarm api GET "/api/v1/compositions/${FORK_ID}/composition.json" --raw > /tmp/composition.json
|
|
673
|
+
|
|
674
|
+
# edit /tmp/composition.html and /tmp/composition.json in your script
|
|
675
|
+
|
|
676
|
+
vidfarm api PUT "/api/v1/compositions/${FORK_ID}/composition.html" --body-file /tmp/composition.html --content-type "text/html; charset=utf-8"
|
|
677
|
+
vidfarm api PATCH "/api/v1/compositions/${FORK_ID}/composition.json" --data-file /tmp/composition.json
|
|
678
|
+
vidfarm api POST "/api/v1/compositions/${FORK_ID}/versions" --data '{"message":"scripting mode snapshot"}'
|
|
679
|
+
vidfarm api POST "/api/v1/compositions/${FORK_ID}/render" --data '{"tracer":"scripting-mode"}'
|
|
680
|
+
```
|
|
681
|
+
|
|
682
|
+
If you need many variants, keep the base fork fixed and fan out by cloning that fork or by reapplying the same edit function to multiple fork ids. Use the raw REST routes directly when you want maximum control; use `vidfarm-devcli` when you want auth, polling, and file helpers without writing the plumbing yourself.
|
|
683
|
+
|
|
607
684
|
## `vidfarm-devcli` — full command surface
|
|
608
685
|
|
|
609
|
-
`@mevdragon/vidfarm-devcli` wraps the **entire director REST flow** as CLI commands. It is a thin shell over the REST API, not a second implementation:
|
|
686
|
+
`@mevdragon/vidfarm-devcli` wraps the **entire director REST flow** as CLI commands. It is a thin shell over the REST API, not a second implementation: most named commands map 1:1 to one REST route, and the file-backed commands compose the documented routes for upload/download + render-polling. Auth via `--api-key <key>` or `VIDFARM_API_KEY`.
|
|
610
687
|
|
|
611
688
|
**Raw REST vs devcli — your choice.** If you want full control, call the REST API directly (or use the raw passthrough `vidfarm api <METHOD> <path> [--data <json>] [--query k=v]`). If you want ergonomics + openable frontend URLs, use the named commands. They are interchangeable — pick per call.
|
|
612
689
|
|
|
690
|
+
**Scripting mode recommendation.** For automation, prefer:
|
|
691
|
+
|
|
692
|
+
- `vidfarm api` for the actual REST calls
|
|
693
|
+
- `--data-file` for file-backed payloads
|
|
694
|
+
- a single pinned base fork id per template family
|
|
695
|
+
- `POST /api/v1/compositions/:forkId/versions` as the versioning boundary
|
|
696
|
+
- `POST /api/v1/compositions/:forkId/render` with a stable `tracer`, or `vidfarm render <forkId> --dir ./work --wait` for the same composed flow
|
|
697
|
+
- `render_target: "cloud"` when you want cloud handoff from a local `vidfarm serve` box, or the default local renderer when you want free in-process output
|
|
698
|
+
|
|
613
699
|
| Command | REST route | Flow step |
|
|
614
700
|
|---|---|---|
|
|
615
701
|
| `vidfarm discover [query]` | `GET /discover/feed[?q=]` | browse/search templates |
|
|
@@ -636,7 +722,7 @@ Send a stable `tracer` on export so retries are traceable and filterable in job
|
|
|
636
722
|
| `vidfarm remove-video-captions <forkId>` (alias: `ghostcut`) | `GET .../compositions/:forkId/remove-video-captions` | subtitle-removal status |
|
|
637
723
|
| `vidfarm snapshot <forkId>` | `POST .../compositions/:forkId/versions` | save composition fork version |
|
|
638
724
|
| `vidfarm versions <forkId>` | `GET .../compositions/:forkId/versions` | list versions |
|
|
639
|
-
| `vidfarm render <forkId> [--wait]` | `POST .../compositions/:forkId/render` | render
|
|
725
|
+
| `vidfarm render <forkId> [--dir <dir\|composition.html>] [--wait] [--target local\|cloud]` | `PUT/PATCH working files, then POST .../compositions/:forkId/render` | script-friendly render to MP4; no `--dir` uses the fork's current working state |
|
|
640
726
|
| `vidfarm render-status <forkId> <renderId>` | `GET .../compositions/:forkId/renders/:renderId` | poll a render |
|
|
641
727
|
| `vidfarm visibility <forkId> <private\|public>` | `PATCH .../compositions/:forkId/visibility` | set visibility |
|
|
642
728
|
| `vidfarm clone <forkId>` | `POST .../compositions/:forkId/clone` | clone a fork |
|
package/SKILL.platform.md
CHANGED
|
@@ -160,7 +160,7 @@ A composition is a single HTML document plus a JSON sidecar.
|
|
|
160
160
|
"summary": "...",
|
|
161
161
|
"scenes": [{ "slug": "hook_reveal", "start": 0, "duration": 1.5, "label": "...", "viral_note": "..." }],
|
|
162
162
|
"captions": [{ "slug": "caption_1", "text": "...", "start": 0, "duration": 1.5, "x": 5, "y": 60 }],
|
|
163
|
-
"viralDna": { "hook": "...", "retention": "...", "payoff": "...", "preserve": [...], "avoid": [...] },
|
|
163
|
+
"viralDna": { "hook": "...", "retention": "...", "payoff": "...", "emotional_punch": { "core_emotion": "...", "tone": "...", "mechanism": "...", "humor": "...", "delivery": "...", "preserve": [...] }, "preserve": [...], "avoid": [...] },
|
|
164
164
|
"width": 1080,
|
|
165
165
|
"height": 1920,
|
|
166
166
|
"durationSeconds": 10.5,
|
|
@@ -205,8 +205,8 @@ Publish flow:
|
|
|
205
205
|
3. API resolves the caller's provider keys (preflight check for required models)
|
|
206
206
|
4. API creates a `job` record and invokes the `hyperframes_render` primitive Lambda
|
|
207
207
|
5. The renderer runs HyperFrames: on the deployed host it stages the composition and dispatches to the shared HyperFrames render state machine (chunked Lambda fan-out); on a local `vidfarm serve` box with no cloud stack it renders in-process (headless Chromium → ffmpeg) for free. See `HyperframesService` in `src/services/hyperframes.ts`.
|
|
208
|
-
6. On success, the primitive returns `{ primary_file_url: "s3://.../output.mp4" }`, the API creates a `ForkVersionRecord` with `reason: "publish"`, writes the snapshot to `versions/<N>/`,
|
|
209
|
-
7. Client polls `GET /api/v1/compositions/:forkId/renders/:renderId` for `{ status: "RUNNING" | "SUCCEEDED" | "FAILED", progress, outputUrl, cost }` (see `adaptJobToPublishStatus` in `src/app.ts`)
|
|
208
|
+
6. On success, the primitive returns `{ primary_file_url: "s3://.../output.mp4" }`, the API creates a `ForkVersionRecord` with `reason: "publish"`, writes the snapshot to `versions/<N>/`, updates the fork's `latestVersion` and `currentRenderJobId`, and now also exposes the deterministic public artifact URL immediately as `expected_output_public_url`
|
|
209
|
+
7. Client polls `GET /api/v1/compositions/:forkId/renders/:renderId` for `{ status: "RUNNING" | "SUCCEEDED" | "FAILED", progress, expectedOutputPublicUrl, outputUrl, cost }` (see `adaptJobToPublishStatus` in `src/app.ts`); `expectedOutputPublicUrl` is the stable public S3 URL, while `outputUrl` remains the completion-time field
|
|
210
210
|
|
|
211
211
|
The HyperFrames render state machine is **shared across all templates and forks** — it's an infrastructure primitive, not a per-template deployment. Its ARN/bucket are passed to the Vidfarm stack as env vars (`HYPERFRAMES_RENDER_*`). Remotion has been fully removed.
|
|
212
212
|
|