@mevdragon/vidfarm-devcli 0.20.6 → 0.20.9

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.
Files changed (157) hide show
  1. package/.agents/skills/editor-capabilities/SKILL.md +15 -1
  2. package/.agents/skills/vidfarm-director/SKILL.md +106 -0
  3. package/.agents/skills/vidfarm-director/recipes/find-and-fork-template.md +16 -0
  4. package/.agents/skills/vidfarm-director/recipes/local-edit-render-approve.md +13 -0
  5. package/.agents/skills/vidfarm-director/recipes/onboard-a-new-director.md +13 -0
  6. package/.agents/skills/vidfarm-director/recipes/retheme-template.md +17 -0
  7. package/.agents/skills/vidfarm-director/references/assets-and-sourcing.md +117 -0
  8. package/.agents/skills/vidfarm-director/references/automation-and-local-dev.md +245 -0
  9. package/.agents/skills/vidfarm-director/references/core-workflows.md +273 -0
  10. package/.agents/skills/vidfarm-director/references/editor-workflows.md +365 -0
  11. package/.agents/skills/vidfarm-director/references/onboarding.md +28 -0
  12. package/.agents/skills/vidfarm-director/references/primitives.md +308 -0
  13. package/SKILL.director.md +517 -250
  14. package/dist/src/cli.js +563 -99
  15. package/dist/src/devcli/clips.js +29 -12
  16. package/dist/src/devcli/doctor.js +2 -2
  17. package/dist/src/devcli/local-backend.js +46 -2
  18. package/dist/src/services/clip-curation/cost.js +5 -1
  19. package/dist/src/services/clip-curation/gemini.js +14 -2
  20. package/dist/src/services/clip-curation/hunt.js +56 -0
  21. package/dist/src/services/clip-curation/index.js +1 -1
  22. package/dist/src/services/clip-curation/scan.js +5 -3
  23. package/package.json +30 -10
  24. package/SKILL.platform.md +0 -432
  25. package/demo/README.md +0 -28
  26. package/demo/dist/app.css +0 -1
  27. package/demo/dist/app.js +0 -1850
  28. package/demo/dist/chunks/chunk-DXB73IDG.js +0 -1
  29. package/demo/dist/chunks/chunk-S7OWAJDS.js +0 -36
  30. package/demo/dist/chunks/chunk-VTIBZ6AN.js +0 -1
  31. package/demo/dist/chunks/dist-ADSJKBVE.js +0 -332
  32. package/demo/dist/chunks/domEditingLayers-VZMLL4AP-SGHWPND4.js +0 -1
  33. package/demo/dist/chunks/hyperframes-player-XB65TCD6.js +0 -425
  34. package/demo/dist/chunks/lib-XAQ37YOE.js +0 -1
  35. package/demo/dist/chunks/src-TJ2QYA4U.js +0 -207
  36. package/demo/dist/favicon.ico +0 -0
  37. package/demo/dist/icons/timeline/audio.svg +0 -7
  38. package/demo/dist/icons/timeline/captions.svg +0 -5
  39. package/demo/dist/icons/timeline/composition.svg +0 -12
  40. package/demo/dist/icons/timeline/image.svg +0 -18
  41. package/demo/dist/icons/timeline/music.svg +0 -10
  42. package/demo/dist/icons/timeline/text.svg +0 -3
  43. package/demo/dist/index.html +0 -15
  44. package/dist/src/account-pages-legacy.js +0 -9396
  45. package/dist/src/account-pages.js +0 -61
  46. package/dist/src/app.js +0 -21603
  47. package/dist/src/composition-runtime.js +0 -1053
  48. package/dist/src/config.js +0 -217
  49. package/dist/src/context.js +0 -447
  50. package/dist/src/dev-app-legacy.js +0 -739
  51. package/dist/src/dev-app.js +0 -6
  52. package/dist/src/devcli/migrate-local.js +0 -140
  53. package/dist/src/devcli/sync.js +0 -368
  54. package/dist/src/domain.js +0 -5
  55. package/dist/src/editor-chat-history.js +0 -82
  56. package/dist/src/editor-chat.js +0 -828
  57. package/dist/src/editor-dark-theme.js +0 -1128
  58. package/dist/src/frontend/debug.js +0 -71
  59. package/dist/src/frontend/discover-client.js +0 -130
  60. package/dist/src/frontend/discover-store.js +0 -23
  61. package/dist/src/frontend/file-directory.js +0 -1018
  62. package/dist/src/frontend/flockposter-cache-store.js +0 -124
  63. package/dist/src/frontend/homepage-client.js +0 -446
  64. package/dist/src/frontend/homepage-shared.js +0 -201
  65. package/dist/src/frontend/homepage-store.js +0 -66
  66. package/dist/src/frontend/homepage-view.js +0 -705
  67. package/dist/src/frontend/page-runtime-client.js +0 -132
  68. package/dist/src/frontend/page-runtime-store.js +0 -9
  69. package/dist/src/frontend/sentry.js +0 -42
  70. package/dist/src/frontend/template-editor-chat.js +0 -4181
  71. package/dist/src/help-page.js +0 -346
  72. package/dist/src/homepage.js +0 -1458
  73. package/dist/src/index.js +0 -16
  74. package/dist/src/instrument.js +0 -30
  75. package/dist/src/landing-page.js +0 -384
  76. package/dist/src/page-runtime.js +0 -2
  77. package/dist/src/page-shell.js +0 -1452
  78. package/dist/src/primitive-context.js +0 -416
  79. package/dist/src/primitive-registry.js +0 -3940
  80. package/dist/src/primitive-sdk.js +0 -4
  81. package/dist/src/primitives/hyperframes-media.js +0 -108
  82. package/dist/src/react-page-shell.js +0 -35
  83. package/dist/src/ready-post-schedule-component.js +0 -1540
  84. package/dist/src/registry.js +0 -296
  85. package/dist/src/reskin/agency-page.js +0 -299
  86. package/dist/src/reskin/calendar-page.js +0 -568
  87. package/dist/src/reskin/chat-page.js +0 -942
  88. package/dist/src/reskin/discover-page.js +0 -1788
  89. package/dist/src/reskin/document.js +0 -1587
  90. package/dist/src/reskin/help-page.js +0 -357
  91. package/dist/src/reskin/index-page.js +0 -62
  92. package/dist/src/reskin/inpaint-clipper-page.js +0 -890
  93. package/dist/src/reskin/inpaint-page.js +0 -2554
  94. package/dist/src/reskin/inpaint-video-page.js +0 -1339
  95. package/dist/src/reskin/job-runs-page.js +0 -477
  96. package/dist/src/reskin/library-page.js +0 -1634
  97. package/dist/src/reskin/login-page.js +0 -262
  98. package/dist/src/reskin/portfolio-page.js +0 -687
  99. package/dist/src/reskin/pricing-page.js +0 -390
  100. package/dist/src/reskin/settings-page.js +0 -732
  101. package/dist/src/reskin/theme.js +0 -711
  102. package/dist/src/runtime.js +0 -35
  103. package/dist/src/services/api-call-history.js +0 -249
  104. package/dist/src/services/auth.js +0 -152
  105. package/dist/src/services/billing-pricing.js +0 -39
  106. package/dist/src/services/billing.js +0 -241
  107. package/dist/src/services/cast.js +0 -127
  108. package/dist/src/services/chat-threads.js +0 -92
  109. package/dist/src/services/clip-records.js +0 -250
  110. package/dist/src/services/clip-search.js +0 -77
  111. package/dist/src/services/clip-vectors.js +0 -125
  112. package/dist/src/services/composition-sanitize.js +0 -124
  113. package/dist/src/services/composition-watch.js +0 -79
  114. package/dist/src/services/elevenlabs.js +0 -222
  115. package/dist/src/services/file-directory.js +0 -117
  116. package/dist/src/services/fork-access.js +0 -93
  117. package/dist/src/services/fork-manifest.js +0 -42
  118. package/dist/src/services/ghostcut.js +0 -179
  119. package/dist/src/services/hyperframes.js +0 -3654
  120. package/dist/src/services/job-capacity.js +0 -14
  121. package/dist/src/services/job-logs.js +0 -197
  122. package/dist/src/services/jobs.js +0 -136
  123. package/dist/src/services/local-dynamo.js +0 -0
  124. package/dist/src/services/media-processing.js +0 -766
  125. package/dist/src/services/primitive-media-lambda.js +0 -280
  126. package/dist/src/services/providers.js +0 -2748
  127. package/dist/src/services/rate-limits.js +0 -262
  128. package/dist/src/services/scene-annotations.js +0 -32
  129. package/dist/src/services/serverless-auth.js +0 -382
  130. package/dist/src/services/serverless-jobs.js +0 -1084
  131. package/dist/src/services/serverless-provider-keys.js +0 -409
  132. package/dist/src/services/serverless-records.js +0 -1515
  133. package/dist/src/services/serverless-template-configs.js +0 -75
  134. package/dist/src/services/storage.js +0 -461
  135. package/dist/src/services/swipe-customize.js +0 -437
  136. package/dist/src/services/template-certification.js +0 -413
  137. package/dist/src/services/template-loader.js +0 -99
  138. package/dist/src/services/template-runtime-bundles.js +0 -217
  139. package/dist/src/services/template-sources.js +0 -1017
  140. package/dist/src/services/upstream.js +0 -248
  141. package/dist/src/services/video-normalization.js +0 -2
  142. package/dist/src/services/webhooks.js +0 -62
  143. package/dist/src/template-editor-pages.js +0 -2576
  144. package/dist/src/template-editor-shell.js +0 -2893
  145. package/dist/src/template-sdk.js +0 -4
  146. package/dist/src/worker.js +0 -17
  147. package/public/assets/discover-client-app.js +0 -1
  148. package/public/assets/file-directory-app.js +0 -3
  149. package/public/assets/homepage-app.js +0 -54
  150. package/public/assets/homepage-client-app.js +0 -80
  151. package/public/assets/page-runtime-client-app.js +0 -94
  152. package/public/assets/placeholders/scene-placeholder.png +0 -0
  153. package/src/assets/SELLING_AWARENESS_STAGES.md +0 -579
  154. package/src/assets/SELLING_WITH_HOOKS.md +0 -377
  155. package/src/assets/SELLING_WITH_VSLS.md +0 -606
  156. package/src/assets/favicon.ico +0 -0
  157. package/src/assets/logo-vidfarm.png +0 -0
@@ -0,0 +1,245 @@
1
+ ## Automate a template via REST
2
+
3
+ Templates now have the same job-backed REST pattern as primitives, so a script can run them repeatably without the editor UI:
4
+
5
+ ```
6
+ POST /api/v1/templates/:templateId/operations/:operationName
7
+ Content-Type: application/json
8
+
9
+ { "tracer": "my-run", "payload": { ... }, "webhook_url": "https://..." }
10
+ ```
11
+
12
+ 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`.
13
+
14
+ ## Automation patterns
15
+
16
+ For agents that operate Vidfarm headlessly, the typical loop is:
17
+
18
+ 1. `POST /api/v1/compositions { template_id }` → fork
19
+ 2. `PUT /api/v1/compositions/:forkId/composition.html` → apply edits
20
+ 3. `PATCH /api/v1/compositions/:forkId/composition.json` → update metadata
21
+ 4. `POST /api/v1/compositions/:forkId/render { tracer }` → render
22
+ 5. Poll `GET /api/v1/compositions/:forkId/renders/:renderId` until `SUCCEEDED`
23
+ 6. Use `expectedOutputPublicUrl` in the next stage when you need the stable public URL immediately; otherwise fall back to `outputUrl` after completion
24
+
25
+ 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.
26
+
27
+ ## Scripting mode
28
+
29
+ **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.
30
+
31
+ Contract:
32
+
33
+ 1. Pick the template once, then fork once and treat the resulting `forkId` as the stable base.
34
+ 2. Read the working state with `GET /api/v1/compositions/:forkId/composition.html` and `GET /api/v1/compositions/:forkId/composition.json`.
35
+ 3. Modify the composition deterministically in your script.
36
+ 4. Write back with `PUT /api/v1/compositions/:forkId/composition.html` and `PATCH /api/v1/compositions/:forkId/composition.json`.
37
+ 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.
38
+ 6. Render with `POST /api/v1/compositions/:forkId/render`, choosing local or cloud based on the host and runtime.
39
+
40
+ Devcli shortcut:
41
+
42
+ ```bash
43
+ vidfarm render "$FORK_ID" --dir ./work --tracer "batch-2026-07-09-row-42" --wait
44
+ ```
45
+
46
+ 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`.
47
+
48
+ Best practices:
49
+
50
+ - Prefer one canonical base fork per automation run, then branch from that fork if you need variants.
51
+ - 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.
52
+ - Keep edits idempotent in your script. Re-run the script against the same fork only if it computes the same desired state.
53
+ - Use a stable `tracer` on render jobs so logs and job history stay searchable.
54
+ - Use the same REST sequence in Lambda, CI, or a local shell. The only thing that changes is the host and auth.
55
+
56
+ Canonical shell shape:
57
+
58
+ ```bash
59
+ BASE_TEMPLATE_ID="template_..."
60
+ FORK_ID="$(vidfarm api POST /api/v1/compositions --data "{\"template_id\":\"${BASE_TEMPLATE_ID}\"}" --json | jq -r '.fork_id')"
61
+
62
+ vidfarm api GET "/api/v1/compositions/${FORK_ID}/composition.html" --raw > /tmp/composition.html
63
+ vidfarm api GET "/api/v1/compositions/${FORK_ID}/composition.json" --raw > /tmp/composition.json
64
+
65
+ # edit /tmp/composition.html and /tmp/composition.json in your script
66
+
67
+ vidfarm api PUT "/api/v1/compositions/${FORK_ID}/composition.html" --body-file /tmp/composition.html --content-type "text/html; charset=utf-8"
68
+ vidfarm api PATCH "/api/v1/compositions/${FORK_ID}/composition.json" --data-file /tmp/composition.json
69
+ vidfarm api POST "/api/v1/compositions/${FORK_ID}/versions" --data '{"message":"scripting mode snapshot"}'
70
+ vidfarm api POST "/api/v1/compositions/${FORK_ID}/render" --data '{"tracer":"scripting-mode"}'
71
+ ```
72
+
73
+ 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.
74
+
75
+ ## `vidfarm-devcli` — full command surface
76
+
77
+ `@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`.
78
+
79
+ **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.
80
+
81
+ **Scripting mode recommendation.** For automation, prefer:
82
+
83
+ - `vidfarm api` for the actual REST calls
84
+ - `--data-file` for file-backed payloads
85
+ - a single pinned base fork id per template family
86
+ - `POST /api/v1/compositions/:forkId/versions` as the versioning boundary
87
+ - `POST /api/v1/compositions/:forkId/render` with a stable `tracer`, or `vidfarm render <forkId> --dir ./work --wait` for the same composed flow
88
+ - `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
89
+
90
+ **Required grounding for AI-authored scripts.** If an AI agent is going to WRITE or MODIFY a local Vidfarm script for a user, the safe default is:
91
+
92
+ 1. `vidfarm pull <forkId> --dir ./work`
93
+ 2. Read `./work/.harness/agent-guide.md`
94
+ 3. Read `./work/.harness/context.json`
95
+ 4. Only then read/edit `composition.html`, `composition.json`, and write the automation logic
96
+
97
+ That is the canonical local scripting path because the pull step now packages:
98
+
99
+ - `video-context.json`: transcript, scene descriptions, viral DNA
100
+ - `editor-harness.json`: technical editing brief (STYLE — *how to edit like this*)
101
+ - `replication-harness.json`: technical replication analysis (BUILD — the 3-paintbrush cheap vs best-quality plans, which beats are raw_clip / hyperframes / reusable_asset / ai_gen, reusable-asset guidance, free-tier note)
102
+ - `scene-annotations.json`: per-scene replacement/recreation DNA
103
+ - `.harness/context.json`: merged agent-facing snapshot (includes `replication_harness`)
104
+ - `.harness/agent-guide.md`: Vidfarm-specific instruction file telling a generic agent how to use the above without conflicting with any repo-owned `AGENTS.md`
105
+
106
+ `agent-guide.md` now renders the **three paintbrushes & two replication harnesses** decomposition CONCRETELY from `replication-harness.json` — the recommended plan's per-beat brush assignment (raw clips vs HTML hyperframes vs reusable assets vs pure AI gen; the (A) cheap-&-efficient default vs (B) best-quality plan), the beats to do in HTML not AI video, the reusable-asset opportunities, and the viral-DNA guardrails — so a desktop agent recreates a template thrift-first (clipping and hyperframes on free compute before any paid generation) and can rebuild it with **no Vidfarm wallet**. See `references/editor-workflows.md` for the full methodology.
107
+
108
+ **Free-tier caveat — the decomposition is the user's job.** The `video-context.json` / `editor-harness.json` / `replication-harness.json` / `scene-annotations.json` grounding files only exist for compositions **Vidfarm has already decomposed** (paid accounts, whose forks pull those files down). On the free tier with no account, `vidfarm pull` won't produce them — **the user and their agent must decompose the reference video themselves** (scenes/audio/text, viral DNA, paintbrush choice) using the method in `references/editor-workflows.md`. Paid Vidfarm accounts get the pre-decomposed viral library and scale-learned prompt-harness best practices instead of doing that pass by hand.
109
+
110
+ If a local AI script rewrites text or scenes without consuming those files first, treat that as a bug in the script/agent flow.
111
+
112
+ | Command | REST route | Flow step |
113
+ |---|---|---|
114
+ | `vidfarm discover [query]` | `GET /discover/feed[?q=]` | browse/search templates |
115
+ | `vidfarm videos [query] [--mine]` | `GET /api/v1/videos[?q=&mine=]` | browse/search source inspirations |
116
+ | `vidfarm inspiration-add <url\|file>` | `POST /discover/templates` (files: `POST /discover/templates/upload/presign` + PUT first) | add your own source (URL or local video upload) |
117
+ | `vidfarm inspiration-rm <id>` | `DELETE /discover/templates/:id` | remove a private one |
118
+ | `vidfarm inspiration-decompose <id>` | `POST /api/v1/inspirations/:id/decompose` | AI-decompose an inspiration |
119
+ | `vidfarm fork <template_id>` | `POST /api/v1/compositions` | fork a template |
120
+ | `vidfarm pull <forkId> [--dir <p>]` | `GET .../compositions/:forkId/{composition.html,json,video-context.json,cast.json,scene-annotations.json,editor-harness.json,replication-harness.json}` | sync a fork to disk + generate `.harness/context.json` and `.harness/agent-guide.md` + print gaps/scene keys + grounding |
121
+ | `vidfarm generate <image\|video> --prompt "…"` | `POST /api/v1/primitives/{images,videos}/generate` (polls job) | generate AI media → finished URL |
122
+ | `vidfarm inpaint <image> --mask <png> --prompt "…" [--region "label=…"] [--ref …] [--out <f>]` | `POST /api/v1/primitives/images/inpaint` (polls job) | masked image EDIT — replace ONLY the transparent-mask region, keep everything else (devcli twin of the /inpaint page) |
123
+ | `vidfarm create-overlay "<subject>" [--key-color #00FF00] [--aspect-ratio 1:1] [--place <dir>] [--out <f>]` | `POST /api/v1/primitives/images/create-overlay` (polls job) | **Vox-style** transparent OVERLAY — AI image on a forced key-color background, chroma-keyed out in one job → ready-to-composite transparent PNG |
124
+ | `vidfarm remove-background-greenscreen <image> [--key-color #00FF00] [--tolerance 0.3] [--out <f>]` | `POST /api/v1/primitives/images/remove-background-greenscreen` (polls job) | cheap chroma-key removal of a FLAT solid background → transparent PNG (cloud primitive) |
125
+ | `vidfarm tts "…" [--style "…"] [--voice <v>] [--out <file>]` | (LOCAL-FIRST: your own OPENAI/GEMINI/OPENROUTER_API_KEY → audio file on disk; `--cloud` = `POST /api/v1/primitives/audio/speech` + poll, ElevenLabs on the platform key by default, `--own-key` for yours) | text → narration audio; `--cloud --voice <voice_id>` picks an ElevenLabs voice |
126
+ | `vidfarm music "<prompt>" [--length <sec>] [--out <f>] [--own-key]` | `POST /api/v1/primitives/music/generate` (polls job) | prompt → music track (ElevenLabs; platform key + wallet by default, `--own-key` for yours) |
127
+ | `vidfarm voices [--own-key] [--limit N]` | `GET /api/v1/primitives/audio/voices` | list ElevenLabs voices (voice_id/name/labels) for `tts --voice`; default a voice + tell the user they can choose |
128
+ | `vidfarm stt <file\|url> [--out <base>] [--no-diarize]` (alias: `transcribe`) | (LOCAL-FIRST: local ffmpeg demux + your own key; `--cloud` = `POST /api/v1/primitives/audio/transcribe` + poll, ElevenLabs Scribe on the platform key by default, `--own-key` for yours) | video/audio → transcript in BOTH formats: simple subtitles (txt + SRT) and multi-speaker segments (json) |
129
+ | `vidfarm place <dir> --src <url\|file> [--at\|--replace]` | (edits local composition.html; local files → serve disk store or temp upload) | drop media (URL **or local file**) into a gap / over a scene |
130
+ | `vidfarm captions generate <dir> [--style <preset>] [--audio <f>\|--srt <f>\|--text "…"]` | (LOCAL-FIRST: STT on your own key — OpenAI = real word timestamps — then edits local composition.html) | transcribe narration → animated word-by-word caption cues |
131
+ | `vidfarm captions style <dir> --style <preset>` / `captions list` / `captions clear` | (edits local composition.html) | restyle / inspect / remove animated captions |
132
+ | `vidfarm keyframes` / `move`\|`nudge` / `ripple` / `trim` / `restack`\|`zindex` `<dir> …` | (edits local composition.html) | script-free CSS keyframe motion + timeline verbs (see "Script-free keyframe motion & timeline verbs") |
133
+ | `vidfarm set-text` / `set-style` / `set-visual` / `set-identity` / `duplicate` / `split` / `retime` / `set-composition` `<dir> …` | (edits local composition.html) | named layer-edit verbs — devcli twins of the web `set_layer_*` / `set_composition` (opacity, line-height, letter-spacing, canvas resize/duration/background); see parity table above |
134
+ | `vidfarm decompose <forkId>` | `POST .../compositions/:forkId/auto-decompose` | split source into scenes |
135
+ | `vidfarm remove-video-captions <forkId>` (alias: `ghostcut`) | `GET .../compositions/:forkId/remove-video-captions` | subtitle-removal status |
136
+ | `vidfarm snapshot <forkId>` | `POST .../compositions/:forkId/versions` | save composition fork version |
137
+ | `vidfarm versions <forkId>` | `GET .../compositions/:forkId/versions` | list versions |
138
+ | `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 |
139
+ | `vidfarm render-status <forkId> <renderId>` | `GET .../compositions/:forkId/renders/:renderId` | poll a render |
140
+ | `vidfarm visibility <forkId> <private\|public>` | `PATCH .../compositions/:forkId/visibility` | set visibility |
141
+ | `vidfarm clone <forkId>` | `POST .../compositions/:forkId/clone` | clone a fork |
142
+ | `vidfarm share-link <forkId>` | `POST .../compositions/:forkId/share-links` | mint a share URL |
143
+ | `vidfarm approve --video <url\|file> --caption "…"` | `POST /api/v1/approved/posts` | approve post (local `--video`/`--media` auto-upload to `temp/`; prints `share_url`) |
144
+ | `vidfarm posts` / `vidfarm post <id>` | `GET /api/v1/approved/posts[/:id]` | browse approved posts |
145
+ | `vidfarm schedule <postId> --at <iso> --to <dest>` | `POST /api/v1/approved/posts/:postId/schedules` | schedule a post |
146
+ | `vidfarm schedules <postId>` | `GET /api/v1/approved/posts/:postId/schedules` | browse scheduled posts |
147
+ | `vidfarm login <email>` / `vidfarm verify <email> <code>` | `POST /api/v1/user/request-otp` · `verify-otp` | get an API key |
148
+ | `vidfarm whoami` | `GET /api/v1/user/me` | who am I |
149
+ | `vidfarm provider-keys` / `vidfarm add-provider-key <p> <secret>` | `GET`·`POST /api/v1/user/me/provider-keys` | manage AI keys |
150
+ | `vidfarm upload <file> [--folder <path>]` | `POST /api/v1/user/me/temporary-files/upload` | upload → durable URL (ephemeral; prefer `--folder temp` for scratch) |
151
+ | `vidfarm download <url> [dest]` | (streams any URL to disk) | download media |
152
+ | `vidfarm download-post <url> [--quality best\|hd\|full_hd]` | `POST /api/v1/primitives/videos/download` + poll | download a social/media post into a durable MP4 or slideshow |
153
+ | `vidfarm download-post-audio <url>` | `POST /api/v1/primitives/audio/download` + poll | download a social/media post's audio into a durable audio file |
154
+ | `vidfarm files [--folder <path>]` | `GET /api/v1/user/me/attachments` | list My Files assets + folders |
155
+ | `vidfarm files --search "…" [--folder <path>]` | `POST /api/v1/user/me/attachments/search` | find My Files assets by MEANING (keyword + vector over name/folder/notes) |
156
+ | `vidfarm get-file <id> [dest] [--print]` | (resolve id → view_url, then stream/print) | read one My Files asset |
157
+ | `vidfarm put-file <file> [--folder <p>] [--as <name>] [--content/--stdin] [--notes "…"]` | `POST /api/v1/user/me/attachments/upload` | write into My Files (persistent) |
158
+ | `vidfarm annotate-file <id\|name> --notes "…"` | `PATCH /api/v1/user/me/attachments/:id` | set metadata notes on one My Files entry (vector-embedded) |
159
+ | `vidfarm raws scan <video> [--range MM:SS-MM:SS] [--duration <s>] [--aspect 9:16] [--no-text] [--prompt "…"]` | (LOCAL: local ffmpeg + local claude/codex agent → `~/.vidfarm` SQLite) | hunt long-form video into short-form raws on this machine |
160
+ | `vidfarm raws scan --cloud <video\|--url <url>> [--tracer <id>]` | temp-file presign/PUT/finalize + `POST /raws/scan` + poll | BACKUP: run the hunt on the deployed pipeline (bills AWS compute only) |
161
+ | `vidfarm clipper <video-url\|file> [--start <t> --end <t>] [--tracer <id> --folder <name> --name <text>]` | (LOCAL-FIRST: stage source into the local backend, then `POST /raws/clip-range`; add `--cloud` for vidfarm.cc) | trim one exact subrange into `/raws`; without `VIDFARM_API_KEY`, URL sources must be saved locally first |
162
+ | `vidfarm raws search "…"` / `raws match "…"` / `raws list` / `raws sources` | (local library; NL→criteria via local agent or provider key) | search/reuse the raws library |
163
+ | `vidfarm raws preset list\|run\|save` / `raws export <ids…> --to <dir>` | (local library) | saved queries; copy raw MP4s out |
164
+ | `vidfarm lint <dir\|composition.html>` | (local static validation) | pre-publish composition check: timing, overlaps, preset names, media src |
165
+ | `vidfarm stills <dir> [--at 0,2.5,…]` | (local in-process render of PNG frames) | visually verify an edit without a full render |
166
+ | `vidfarm doctor` | (local environment triage) | check ffmpeg/node/keys/agent CLI/poisoned env before debugging anything else |
167
+ | `vidfarm skills list\|add <name>\|update` | `GET /skill-pack/index.json` · `/skill-pack/:name/*` | install/refresh skill packs (see "Skill packs — import on demand") |
168
+ | `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 |
169
+ | `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`) |
170
+ | `vidfarm capture <url>` | (local headless-Chrome capture) | website screenshots/assets for website-to-video flows |
171
+ | `vidfarm beats <audio>` | (local beat detection → JSON) | music beat timestamps to sync cuts/transitions/captions |
172
+
173
+ **Frontend URLs are first-class output.** Every command that has a human-openable page (editor, discover, approved-post preview, share link) prints that prod frontend URL as a highlighted line. `render --wait` polls to completion and prints the final MP4 URL; `approve` prints the approved-post `share_url`. Add `--json` to any command for pure JSON (agent-friendly, no banners).
174
+
175
+ **Uploads/downloads/My-Files writes live in the devcli** because they are genuine multi-step / streaming flows: `upload` posts the file as multipart to the **ephemeral** temporary-files route and prints the durable URL to drop into a composition or approved post; `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.
176
+
177
+ ## Local dev loop (`vidfarm serve`)
178
+
179
+ Run the **full** editor locally so a coding agent (Claude Code / Codex) edits composition files on disk while a human finishes in the browser — one source of truth, live sync both ways.
180
+
181
+ ```bash
182
+ npx -y @mevdragon/vidfarm-devcli serve <template_id>
183
+ # → boots the full backend on http://localhost:3000 (records + storage on disk)
184
+ # → pulls that template's default fork (or --fork <id>) from the cloud onto disk
185
+ # → auto-provisions a local user and opens the editor, pre-authed
186
+ ```
187
+
188
+ 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.
189
+
190
+ **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 HyperFrames 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.
191
+
192
+ How the loop works:
193
+ - 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.
194
+ - When the agent saves it, the server's `fs.watch` fans a `reload` for that fork over **same-origin** SSE (`GET /api/v1/dev/events`) and the open editor tab live-morphs the change — no reload, playback state preserved (keyed DOM morph on `data-hf-id`).
195
+ - When the human edits in the browser, the editor `PUT`s the composition back to the same file; the server suppresses that self-write so it doesn't echo. Multiple forks can be edited at once under one server.
196
+
197
+ 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`.
198
+
199
+ 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>`.
200
+
201
+ 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>`.
202
+
203
+ ## Skill packs — import on demand (HyperFrames-grade authoring power)
204
+
205
+ This skill stays lean on purpose. Deep authoring craft lives in **skill packs** — vidfarm-scrubbed snapshots of the HyperFrames skill suite plus vidfarm's own media pack — vendored on the Vidfarm host and installed only when a task needs them. Never install skills from upstream vendor orgs or third-party registries; the vidfarm mirror is the source (`vidfarm skills add <name>` fetches `GET /skill-pack/:name/*` with hash verification into `.agents/skills/` + a `.claude/skills/` link, pinned in `skills-lock.json`; `vidfarm skills list` shows what is available/installed; `vidfarm skills update` refreshes pins).
206
+
207
+ Import by task:
208
+
209
+ | When the task needs… | Install |
210
+ |---|---|
211
+ | the raw composition HTML contract (data-* attrs, tracks, clips, sub-compositions, determinism rules) | `vidfarm skills add hyperframes-core` |
212
+ | motion design: animation rules, multi-phase scene blueprints, transition doctrine, runtime adapters (GSAP/Lottie/Three/Anime/CSS/WAAPI/TypeGPU) | `vidfarm skills add hyperframes-animation` |
213
+ | seek-safe keyframe patterns (FLIP, paths, masks, SVG draw/morph, 3D depth) | `vidfarm skills add hyperframes-keyframes` |
214
+ | creative direction: design specs, palettes, typography, house style, narration craft | `vidfarm skills add hyperframes-creative` |
215
+ | the HyperFrames CLI dev loop (init/lint/validate/inspect/snapshot/preview/render) | `vidfarm skills add hyperframes-cli` |
216
+ | narration/BGM/SFX/media resolution (the shared audio engine) | `vidfarm skills add vidfarm-media` |
217
+ | embedded/cinematic captions with subject occlusion (32-identity catalog) | `vidfarm skills add embedded-captions` |
218
+ | graphic overlay cards on existing talking-head footage | `vidfarm skills add talking-head-recut` |
219
+ | a product launch/promo video end-to-end | `vidfarm skills add product-launch-video` |
220
+ | a faceless topic explainer end-to-end | `vidfarm skills add faceless-explainer` |
221
+ | a website tour/showcase video end-to-end | `vidfarm skills add website-to-video` |
222
+ | a short motion graphic (kinetic type, stat hit, logo sting, lower-third) | `vidfarm skills add motion-graphics` |
223
+ | an interactive slideshow/deck | `vidfarm skills add slideshow` |
224
+ | any other multi-scene composition (fallback workflow) | `vidfarm skills add general-video` |
225
+
226
+ Ground rules:
227
+
228
+ - **Local coding agents** (Claude Code / Codex on a `vidfarm serve` box or a pulled fork) get the FULL packs — install, read, and follow them. Scripted/GSAP compositions authored this way render correctly through `vidfarm render` (local and cloud); note that the **web editor strips `<script>` on save**, so keep compositions that must round-trip through the browser editor declarative (the built-in Ken Burns / transitions / animated-captions vocabulary).
229
+ - **The web copilot** never installs packs — it has a `load_skill` tool that reads the same content on demand from the vidfarm mirror (served from `.agents/skills/`; the `.claude/skills/*` entries are just Claude Code discovery symlinks to the same dirs). It can `load_skill` any hyperframes pack (`hyperframes`, `hyperframes-core`, `hyperframes-animation`, `hyperframes-keyframes`, `hyperframes-creative`, `hyperframes-cli`) plus the workflow packs (`embedded-captions`, `product-launch-video`, `faceless-explainer`, `website-to-video`, `general-video`, `motion-graphics`, `slideshow`, `talking-head-recut`, `vidfarm-media`). Nothing to install; it is already wired. **In the web editor, apply only the CSS / `@keyframes` + declarative-preset half of `hyperframes-animation` / `hyperframes-keyframes`** — their JS-adapter techniques are devcli-only (script is stripped on save).
230
+ - Vidfarm-managed environments set `HYPERFRAMES_SKIP_SKILLS=1` and `HYPERFRAMES_NO_TELEMETRY=1`, so `npx hyperframes init` never overwrites the vidfarm-scrubbed packs and the bundled CLI never phones home. Do not run `hyperframes auth`, `hyperframes cloud`, `hyperframes publish`, `hyperframes play`, or `hyperframes feedback` — vidfarm's own render/share/telemetry surfaces cover all of them.
231
+
232
+ ## What NOT to do
233
+
234
+ - Do **not** manipulate composition HTML by string concatenation. Always parse, edit, and re-serialize the DOM. The editor and runtime rely on `data-start`, `data-duration`, `data-track-index`, `data-hf-id`, `data-composition-id` attributes being well-formed.
235
+ - Do **not** save AI provider keys in the composition HTML or JSON. They belong in the caller's provider-keys record.
236
+ - Do **not** call the renderer directly. Publishing goes through `POST /api/v1/compositions/:forkId/render` so billing, retries, cost caps, and Lambda quotas are enforced.
237
+ - Do **not** re-invent scene manipulation as REST commands (`AddScene`, `RemoveScene`, `UpdateSceneField`). The Trackpad Editor is the surface for that. The pattern is: mutate the composition HTML DOM, PUT it back.
238
+
239
+ ## Alpha posture
240
+
241
+ Vidfarm is in active development. Endpoints, response shapes, and the editor UI can change. When something behaves unexpectedly:
242
+
243
+ - prefer `GET /api/v1/compositions/:forkId` over cached state
244
+ - treat `latest_version` as the source of truth for what the editor loaded
245
+ - if a publish fails, check `renders/:renderId` for the error phase and stderr before retrying
@@ -0,0 +1,273 @@
1
+ ## Mental model
2
+
3
+ A Vidfarm composition is an **HTML document + JSON metadata**. Each layer (video clip, audio, image, text, caption) is a timed DOM element with `data-start`, `data-duration`, `data-track-index` attributes. The Trackpad Editor is a timeline UI over this document, similar to Premiere or DaVinci.
4
+
5
+ Every director workflow goes through three concepts:
6
+
7
+ 1. **Template** — a published composition anyone can fork. Template ids start with `template_`; each template opens in the editor at `/editor/:templateId` and has a source video, a decomposed timeline, and metadata (viral DNA, captions, scenes).
8
+ 2. **Composition fork** — a private editable copy of a template. Every fork has a `forkId`, an owner, a working state, and a version history. The Trackpad Editor operates on a fork.
9
+ 3. **Publish** — render the current fork state to MP4 via a Lambda render job. Each publish snapshots the current working state as an immutable version.
10
+
11
+ The Trackpad Editor is the primary surface. Most director actions happen there. This skill's REST endpoints exist for automation, CI, and headless flows.
12
+
13
+ ## Base URL and auth
14
+
15
+ The current API base URLs are:
16
+
17
+ - staging: `https://staging.vidfarm.cc`
18
+ - production: `https://vidfarm.cc`
19
+
20
+ Every browser action is authenticated by the session cookie set at `/login`. Headless API-key auth uses the `vidfarm-api-key` header set to the caller's API key (from Settings → API Keys).
21
+
22
+ **Auth contract: API-key auth is the `vidfarm-api-key: <key>` request header and nothing else. `Authorization: Bearer <key>` is NOT supported — the server never reads the `authorization` header (see `requireAuth` in `src/app.ts`).**
23
+
24
+ ```
25
+ vidfarm-api-key: vf_key_<id>
26
+ ```
27
+
28
+ Scope note (verified against `src/app.ts`): the `vidfarm-api-key` header authenticates **every** authenticated surface — the `requireAuth` routes (`/api/v1/user/*`, `/api/v1/agency/*`, `/api/v1/templates/*`, `/api/v1/primitives/*`, `/api/v1/rate-limit-status`, editor-chat threads) **and** the `/api/v1/compositions/*` + `/api/v1/videos/*` + editor page routes, where an explicit API key takes precedence over any browser session cookie. Cookies remain how the web UI authenticates; headless callers never need one. Reads on composition routes also work without any credential because the `forkId` acts as an unguessable view bearer token, and share-link tokens (header `vidfarm-share-token` or `?share=` query) can grant `viewer`/`editor`/`publisher` roles.
29
+
30
+ ## Login flow
31
+
32
+ Directors log in with OTP:
33
+
34
+ 1. `POST /api/v1/user/request-otp { email }` — sends a code to email; returns `{ ok, delivery }` (plus `dev_code` when delivery is `console` outside production)
35
+ 2. `POST /api/v1/user/verify-otp { email, code, name? }` — returns `{ customer, apiKey }` and mints a fresh API key
36
+
37
+ Save the `apiKey` in the client and use it for automation (as the `vidfarm-api-key` header). The browser login form uses the session-cookie variants `POST /login/otp/request` and `POST /login/otp/verify` instead.
38
+
39
+ ## Provider keys
40
+
41
+ Vidfarm executes many features (smart auto-decompose, editor chat) with the **caller's own AI provider keys**. Directors save keys once via:
42
+
43
+ - UI: Settings → Provider Keys
44
+ - API: `POST /api/v1/user/me/provider-keys { provider, secret, label? }` — one key per call; `provider` is `"openai" | "gemini" | "openrouter" | "perplexity"`. The key is validated against the provider before it is saved. List saved keys with `GET /api/v1/user/me/provider-keys`.
45
+
46
+ Priority: Gemini → OpenAI → OpenRouter. If a director has no keys, smart decompose falls back to a deterministic time-based split.
47
+
48
+ ## Discover templates
49
+
50
+ Templates are listed on the Vidfarm homepage and `/discover`. Each has a `template_...` id, a preview MP4, source video metadata, and viral DNA.
51
+
52
+ - Browser: browse `/discover`
53
+ - API: `GET /discover/feed` — returns `{ templates: [{ templateId, slugId, title, previewUrl, viralDna, durationSeconds, sourceType, promotions, keywords, summary, ... }], next_cursor }`
54
+ - Search: `GET /discover/feed?q=<offer>&limit=20` keyword-searches the catalog so you can answer "which templates suit my `<offer>`?" (e.g. `?q=weight%20loss%20app`). Ranking is a coarse keyword match over each template's title, viral DNA, and the decompose-derived `promotions` (offer categories the format can sell), `keywords`, and `summary` — read those fields to judge fit and rank the best 3-6 semantically. `GET /api/v1/videos?q=<offer>&limit=20[&mine=true]` runs the same search over source **inspirations**. `promotions`/`keywords`/`summary` are populated by the decompose pass, so a freshly ingested template that has not been decomposed yet won't match on them — decompose it (or fall back to broader/synonym terms) if a search comes up empty.
55
+
56
+ Each template exposes a public preview:
57
+
58
+ - `GET /editor/:templateId` — opens the Trackpad Editor for the template (redirects to your fork of it, or to `/login`)
59
+ - `GET /editor/:templateId/composition` — raw composition HTML built from the template's source video
60
+ - `GET /discover/skills/:templateId` — the template's SKILL.md content as JSON (registry templates only)
61
+
62
+ Directors don't edit templates directly. They fork.
63
+
64
+ ### Add your own source (inspiration)
65
+
66
+ To bring a new viral video into the catalog as a **private** template you own, ingest its social URL — or upload a video file directly:
67
+
68
+ - `POST /discover/templates { source_url, tagline?, notes? }` — accepts TikTok / YouTube / Instagram / Twitter-X URLs only. The video download runs async; the response is the inspiration record (`202`, `status: "processing"`). The card finalizes into a private `template_...` the next time `GET /discover/feed` is polled (the feed poll doubles as the completion check).
69
+ - **Upload a file** (three steps, same finalize behavior as the URL flow):
70
+ 1. `POST /discover/templates/upload/presign { file_name, content_type?, size_bytes? }` — video files only (MP4/MOV/WebM), 200 MB cap. Returns `transport: "presigned"` with a `{ upload: { method, url, headers } }` S3 PUT target, or `transport: "server"` (local-storage boxes) pointing at the multipart fallback below. Both include the `storage_key` to finalize with.
71
+ 2. Send the bytes: `PUT` them to the presigned URL, or `POST /discover/templates/upload` (multipart, field `file`) when transport is `server`.
72
+ 3. `POST /discover/templates { upload: { storage_key, file_name }, title?, tagline?, notes? }` — queues an async `video_ingest` job (durable mirror + duration probe + thumbnail) and returns the inspiration record (`202`). Finalizes into a private template exactly like the URL flow. `title` names the /discover card; untitled templates display as their `template_...` id (the file name is only shown while processing, never persisted as the title).
73
+ - `DELETE /discover/templates/:entryId` — remove a private inspiration/template you own (accepts either the `inspiration_...` or minted `template_...` id).
74
+ - `POST /api/v1/inspirations/:inspirationId/decompose { user_prompt? }` — AI-decompose an inspiration's downloaded video into scenes (requires a saved provider key; same 120s source cap as auto-decompose).
75
+
76
+ devcli: `vidfarm inspiration-add <url|file.mp4>` (a local file path runs the presign→PUT→finalize flow for you), `vidfarm inspiration-rm <id>`, `vidfarm inspiration-decompose <id>`, `vidfarm discover [query]` to browse/search templates, `vidfarm public-raws [query] [--category <key>] [--type <raw_type>] [--bookmark <raw_id>]` to browse/save the public raws catalog, and `vidfarm videos [query] [--mine]` to browse/search source inspirations. On the web, the Discover page's **Add Template** modal accepts either a URL or a file upload.
77
+
78
+ ## Fork a template
79
+
80
+ ```
81
+ POST /api/v1/compositions
82
+ Content-Type: application/json
83
+
84
+ { "template_id": "template_<...>", "title": "Optional title" }
85
+ ```
86
+
87
+ `template_id` is required and must start with `template_` (the handler also accepts the same value under the key `source`). Returns the new fork's metadata (201):
88
+
89
+ ```
90
+ { "fork_id": "<forkId>", "template_id": "template_...", "parent_fork_id": "...", "parent_version": 1, "title": "...", "visibility": "private", "latest_version": 0, "composition_url": "...", "composition_data_url": "...", "versions_url": "...", "role": "owner", "capabilities": { ... } }
91
+ ```
92
+
93
+ Take the returned `fork_id` and open the Trackpad Editor at:
94
+
95
+ ```
96
+ /editor/<templateId>/fork/<forkId>
97
+ ```
98
+
99
+ The editor loads `composition.html` and `composition.json` from the fork's working state.
100
+
101
+ ### New fork from an existing fork (branch this copy, or start fresh)
102
+
103
+ Once you already hold a fork, spawn ANOTHER editable copy — a branch — with one unified endpoint. This is what the editor's ⋯ menu **New fork** modal and the devcli `clone --from` command both call:
104
+
105
+ ```
106
+ POST /api/v1/compositions/:forkId/fork
107
+ Content-Type: application/json
108
+
109
+ { "from": "current" | "default", "parent_version": <optional N>, "title": "Optional" }
110
+ ```
111
+
112
+ - `from: "current"` (default) — branch a new private fork from **this** fork's working copy; all current edits carry over. Optionally pin `parent_version` to clone a saved version instead of the live working state. (The legacy `POST /:forkId/clone` route is an alias for exactly this.)
113
+ - `from: "default"` — start a **fresh** fork from the template's canonical default composition, discarding the current fork's edits (they stay in the fork you branched from).
114
+
115
+ 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`).
116
+
117
+ ## Render (publish to MP4)
118
+
119
+ 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`.
120
+
121
+ ```
122
+ POST /api/v1/compositions/:forkId/render
123
+ Content-Type: application/json
124
+
125
+ { "title": "optional", "version": null, "tracer": "optional-trace-id", "html": "optional HTML to save to working state before rendering" }
126
+ ```
127
+
128
+ 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:
129
+
130
+ ```
131
+ GET /api/v1/compositions/:forkId/renders/:renderId
132
+ ```
133
+
134
+ 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.
135
+
136
+ Every publish creates an immutable version snapshot at `versions/<N>/composition.html` and `versions/<N>/composition.json`.
137
+
138
+ 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.
139
+
140
+ ## Approve a finished post
141
+
142
+ 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.
143
+
144
+ ```
145
+ POST /api/v1/approved/posts
146
+ Content-Type: application/json
147
+
148
+ { "caption": "required", "title": "optional", "pinned_comment": "optional", "tracer": "optional",
149
+ "media": [ { "url": "https://.../output.mp4", "kind": "video", "role": "primary" } ] }
150
+ ```
151
+
152
+ `caption` is required. `media[]` items take `{ url, kind?: "image"|"video"|..., role?: "primary"|"slide"|... }`. Response (`201`) is the approved post including **`share_url`** — the prod frontend page for previewing/sharing. Surface that URL to the user; it is the headline output of this step.
153
+
154
+ - `GET /api/v1/approved/posts` — list your approved posts
155
+ - `GET /api/v1/approved/posts/:postId` — read one (returns `share_url`, `download_zip_url`)
156
+
157
+ devcli: `vidfarm approve --video <mp4-url> --caption "..."` prints the `share_url` as a first-class openable link; `vidfarm posts` lists, `vidfarm post <id>` reads one.
158
+
159
+ ## Schedule a post
160
+
161
+ Schedule an approved post to a connected destination channel (FlockPoster social account or email) at one ISO timestamp:
162
+
163
+ ```
164
+ POST /api/v1/approved/posts/:postId/schedules
165
+ Content-Type: application/json
166
+
167
+ { "destination_type": "flockposter" | "email", "destination_id": "<channel or email>",
168
+ "scheduled_at": "2026-07-10T14:00:00Z", "timezone": "America/New_York", "additional_notes": "optional" }
169
+ ```
170
+
171
+ Minimum 10-minute lead time. Response (`201`) is the schedule record. Browse existing schedules with `GET /api/v1/approved/posts/:postId/schedules`.
172
+
173
+ devcli: `vidfarm schedule <postId> --at <iso> --to <destinationId> [--type flockposter|email]`, and `vidfarm schedules <postId>` to browse.
174
+
175
+ ## Version history
176
+
177
+ ```
178
+ GET /api/v1/compositions/:forkId/versions?limit=100
179
+ ```
180
+
181
+ Returns `{ versions: [{ version, reason, message, created_by, created_at, render_job_id, composition_url, composition_data_url }], next_cursor }`. Reason is `publish`, `clone`, `manual`, or `migration`. To snapshot the working state manually, `POST /api/v1/compositions/:forkId/versions { "message": "optional" }`.
182
+
183
+ To view a past cut without reverting:
184
+
185
+ ```
186
+ GET /api/v1/compositions/:forkId/versions/:version/composition.html
187
+ ```
188
+
189
+ To revert the working state to a version, `PUT` the historical HTML/JSON back to the working keys.
190
+
191
+ ## Sharing and visibility
192
+
193
+ Every fork has a visibility: `private` (default), `unlisted` (legacy), or `public` (view-only).
194
+
195
+ ```
196
+ PATCH /api/v1/compositions/:forkId/visibility { "visibility": "public" }
197
+ ```
198
+
199
+ Public forks are accessible at `/editor/<templateId>?fork=<forkId>` (view-only for non-owners). Public visibility is the simplest sharing surface.
200
+
201
+ Fine-grained sharing uses per-user permissions and share links (all body fields are snake_case):
202
+
203
+ - `POST /api/v1/compositions/:forkId/permissions { grantee_email | grantee_customer_id, role, expires_at? }` — grant a specific registered user access. Exactly one of `grantee_email` or `grantee_customer_id` is required. Roles: `viewer`, `editor`, `publisher`. Returns `{ permission_id, fork_id, grantee_type, grantee_id, role, granted_by, expires_at, created_at }`.
204
+ - `POST /api/v1/compositions/:forkId/share-links { role, expires_at? }` — create a token URL. Anyone with the token gets the role. Returns `{ token, share_url, role, expires_at, ... }`.
205
+ - `GET /api/v1/compositions/shared/:token` — access via share link
206
+ - `DELETE /api/v1/compositions/:forkId/permissions/:permissionId` — revoke a grant
207
+ - `DELETE /api/v1/compositions/:forkId/share-links/:token` — revoke a link
208
+
209
+ ## Cloning a fork
210
+
211
+ Directors can clone another fork (their own or a shared one) as a new starting point:
212
+
213
+ ```
214
+ POST /api/v1/compositions/:forkId/clone
215
+ Content-Type: application/json
216
+
217
+ { "parent_version": "<N or omit for latest>", "title": "optional" }
218
+ ```
219
+
220
+ The `:forkId` in the path is the fork being cloned. The new fork inherits the parent's HTML/JSON at the specified version and creates a version-1 snapshot with `reason: "clone"`.
221
+
222
+ ## Delete a fork
223
+
224
+ ```
225
+ DELETE /api/v1/compositions/:forkId
226
+ ```
227
+
228
+ Soft-delete only. The fork's `deletedAt` is set. Versions and share links are preserved for audit.
229
+
230
+ ## Cost spectrum (free → $10+/video) — default to saving the director money
231
+
232
+ There is no single price for a video. The **approach** the director picks sets the cost, across a wide spectrum. Always steer toward the cheapest approach that still meets their goal, and make the tradeoff explicit rather than silently choosing an expensive path.
233
+
234
+ | Approach | Typical cost | How |
235
+ |---|---|---|
236
+ | **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 HyperFrames render — free and unguarded, no cloud). |
237
+ | **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. |
238
+ | **AI-generate some scenes** | **~$1** | Replace a few scenes with AI-generated video clips for high specificity/customization (see the "Generate AI media" section). |
239
+ | **Heavy AI generation** | **$10+** | Many/long AI video clips, custom characters, fully bespoke scenes. |
240
+
241
+ Rules of thumb:
242
+
243
+ - **Reusing existing footage — or footage the director films themselves — is the cheapest.** AI-generating characters/scenes is the most expensive. Editing captions/images on a reused template is nearly free.
244
+ - **Local render is free; cloud render costs pennies.** If the director is iterating with a coding agent on a `vidfarm serve` box, they can render locally at no charge until they want the durable cloud MP4.
245
+ - **Decompose is a one-time ~$0.10** (smart decompose ≈ provider passthrough + GhostCut ~$0.10/30s) per **new** source video — but the director can **skip it entirely** by forking a template that's **already decomposed** in the catalog.
246
+ - **Image generation is cheap** — use it freely, no need to ask permission.
247
+ - **AI video generation is expensive — ask the director's permission before using it.** Default to reuse/local/image approaches unless they've okayed video gen or told you the budget covers it.
248
+ - **Ask about budget.** During Getting Started (and whenever it's relevant per editor project, or when the director asks about cost), ask roughly what they want to spend per video, and pick the approach band that fits. If they haven't said, assume the cheapest approach that works.
249
+
250
+ The director always chooses the method that works for them — the point is to surface the tradeoff and default to thrift, not to force the cheapest path.
251
+
252
+ **How the approach is painted — the three paintbrushes.** The cost of a video is set by *which brush* recreates each scene: (1) **raw clips** remixed from existing footage, (2) **HTML/JS hyperframes** (animated text/images/graphics), (3) **pure AI generation** (image/video/voice/music, most expensive). A technical replication decomposition names, per beat, when/where/what each brush should be, and offers two harnesses — **(A) cheap & efficient** (recaption, bg+fg video memes, hyperframe animation, reuse/greenscreen the asset library, AI only if needed) and **(B) best quality** (AI video by default, AI-image storyboarding first, adversarial grading with a coding agent). Default to (A); reserve (B) for premium/budgeted work. Full methodology in `references/editor-workflows.md` (“The three paintbrushes & two replication harnesses”).
253
+
254
+ ## Billing
255
+
256
+ Vidfarm charges directly in USD from the caller's wallet. There are no credits.
257
+
258
+ - **Wallet top-up** — Stripe checkout via the Settings UI, backed by `POST /settings/wallet/funding-link` (browser session; returns `{ checkout_url, client_reference_id }`)
259
+ - **Balance & history** — shown on the Settings page; billing events via `GET /u/:customerId/settings/wallet/events` (browser session). There is no `/api/v1/user/me/wallet` REST endpoint.
260
+ - **Cost anchors** (see the Cost spectrum section above for how these combine per approach):
261
+ - Local render on a `vidfarm serve` box (in-process HyperFrames): **free**
262
+ - Cloud render (HyperFrames Lambda fan-out): typically $0.01 – $0.10 per MP4 depending on length/resolution
263
+ - Image generation / edit: cheap (provider passthrough) — use freely
264
+ - AI **video** generation: expensive ($1–$10+/video territory) — ask permission first
265
+ - Auto-decompose smart mode: pass-through of caller's AI provider spend (~$0.10 one-time with GhostCut), or skip it by forking an already-decomposed template
266
+ - Auto-decompose time-slice: free
267
+ - GhostCut subtitle removal: ~$0.10 per 30 seconds of source video
268
+
269
+ Framing:
270
+
271
+ - Vidfarm is optimized for cost efficiency, not markup
272
+ - Total director cost per video is typically far below alternatives
273
+ - Platform retains a small safety buffer for operational correctness