@mevdragon/vidfarm-devcli 0.20.6 → 0.20.8
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 +15 -1
- package/.agents/skills/vidfarm-director/SKILL.md +106 -0
- package/.agents/skills/vidfarm-director/recipes/find-and-fork-template.md +16 -0
- package/.agents/skills/vidfarm-director/recipes/local-edit-render-approve.md +13 -0
- package/.agents/skills/vidfarm-director/recipes/onboard-a-new-director.md +13 -0
- package/.agents/skills/vidfarm-director/recipes/retheme-template.md +17 -0
- package/.agents/skills/vidfarm-director/references/assets-and-sourcing.md +111 -0
- package/.agents/skills/vidfarm-director/references/automation-and-local-dev.md +243 -0
- package/.agents/skills/vidfarm-director/references/core-workflows.md +273 -0
- package/.agents/skills/vidfarm-director/references/editor-workflows.md +365 -0
- package/.agents/skills/vidfarm-director/references/onboarding.md +28 -0
- package/.agents/skills/vidfarm-director/references/primitives.md +265 -0
- package/SKILL.director.md +476 -260
- package/dist/src/cli.js +362 -96
- package/dist/src/devcli/clips.js +15 -10
- package/dist/src/devcli/doctor.js +2 -2
- package/dist/src/devcli/local-backend.js +46 -2
- package/dist/src/services/clip-curation/cost.js +5 -1
- package/dist/src/services/clip-curation/gemini.js +10 -0
- package/package.json +30 -10
- package/SKILL.platform.md +0 -432
- package/demo/README.md +0 -28
- package/demo/dist/app.css +0 -1
- package/demo/dist/app.js +0 -1850
- package/demo/dist/chunks/chunk-DXB73IDG.js +0 -1
- package/demo/dist/chunks/chunk-S7OWAJDS.js +0 -36
- package/demo/dist/chunks/chunk-VTIBZ6AN.js +0 -1
- package/demo/dist/chunks/dist-ADSJKBVE.js +0 -332
- package/demo/dist/chunks/domEditingLayers-VZMLL4AP-SGHWPND4.js +0 -1
- package/demo/dist/chunks/hyperframes-player-XB65TCD6.js +0 -425
- package/demo/dist/chunks/lib-XAQ37YOE.js +0 -1
- package/demo/dist/chunks/src-TJ2QYA4U.js +0 -207
- package/demo/dist/favicon.ico +0 -0
- package/demo/dist/icons/timeline/audio.svg +0 -7
- package/demo/dist/icons/timeline/captions.svg +0 -5
- package/demo/dist/icons/timeline/composition.svg +0 -12
- package/demo/dist/icons/timeline/image.svg +0 -18
- package/demo/dist/icons/timeline/music.svg +0 -10
- package/demo/dist/icons/timeline/text.svg +0 -3
- package/demo/dist/index.html +0 -15
- package/dist/src/account-pages-legacy.js +0 -9396
- package/dist/src/account-pages.js +0 -61
- package/dist/src/app.js +0 -21603
- package/dist/src/composition-runtime.js +0 -1053
- package/dist/src/config.js +0 -217
- package/dist/src/context.js +0 -447
- package/dist/src/dev-app-legacy.js +0 -739
- package/dist/src/dev-app.js +0 -6
- package/dist/src/devcli/migrate-local.js +0 -140
- package/dist/src/devcli/sync.js +0 -368
- package/dist/src/domain.js +0 -5
- package/dist/src/editor-chat-history.js +0 -82
- package/dist/src/editor-chat.js +0 -828
- package/dist/src/editor-dark-theme.js +0 -1128
- package/dist/src/frontend/debug.js +0 -71
- package/dist/src/frontend/discover-client.js +0 -130
- package/dist/src/frontend/discover-store.js +0 -23
- package/dist/src/frontend/file-directory.js +0 -1018
- package/dist/src/frontend/flockposter-cache-store.js +0 -124
- package/dist/src/frontend/homepage-client.js +0 -446
- package/dist/src/frontend/homepage-shared.js +0 -201
- package/dist/src/frontend/homepage-store.js +0 -66
- package/dist/src/frontend/homepage-view.js +0 -705
- package/dist/src/frontend/page-runtime-client.js +0 -132
- package/dist/src/frontend/page-runtime-store.js +0 -9
- package/dist/src/frontend/sentry.js +0 -42
- package/dist/src/frontend/template-editor-chat.js +0 -4181
- package/dist/src/help-page.js +0 -346
- package/dist/src/homepage.js +0 -1458
- package/dist/src/index.js +0 -16
- package/dist/src/instrument.js +0 -30
- package/dist/src/landing-page.js +0 -384
- package/dist/src/page-runtime.js +0 -2
- package/dist/src/page-shell.js +0 -1452
- package/dist/src/primitive-context.js +0 -416
- package/dist/src/primitive-registry.js +0 -3940
- package/dist/src/primitive-sdk.js +0 -4
- package/dist/src/primitives/hyperframes-media.js +0 -108
- package/dist/src/react-page-shell.js +0 -35
- package/dist/src/ready-post-schedule-component.js +0 -1540
- package/dist/src/registry.js +0 -296
- package/dist/src/reskin/agency-page.js +0 -299
- package/dist/src/reskin/calendar-page.js +0 -568
- package/dist/src/reskin/chat-page.js +0 -942
- package/dist/src/reskin/discover-page.js +0 -1788
- package/dist/src/reskin/document.js +0 -1587
- package/dist/src/reskin/help-page.js +0 -357
- package/dist/src/reskin/index-page.js +0 -62
- package/dist/src/reskin/inpaint-clipper-page.js +0 -890
- package/dist/src/reskin/inpaint-page.js +0 -2554
- package/dist/src/reskin/inpaint-video-page.js +0 -1339
- package/dist/src/reskin/job-runs-page.js +0 -477
- package/dist/src/reskin/library-page.js +0 -1634
- package/dist/src/reskin/login-page.js +0 -262
- package/dist/src/reskin/portfolio-page.js +0 -687
- package/dist/src/reskin/pricing-page.js +0 -390
- package/dist/src/reskin/settings-page.js +0 -732
- package/dist/src/reskin/theme.js +0 -711
- package/dist/src/runtime.js +0 -35
- package/dist/src/services/api-call-history.js +0 -249
- package/dist/src/services/auth.js +0 -152
- package/dist/src/services/billing-pricing.js +0 -39
- package/dist/src/services/billing.js +0 -241
- package/dist/src/services/cast.js +0 -127
- package/dist/src/services/chat-threads.js +0 -92
- package/dist/src/services/clip-records.js +0 -250
- package/dist/src/services/clip-search.js +0 -77
- package/dist/src/services/clip-vectors.js +0 -125
- package/dist/src/services/composition-sanitize.js +0 -124
- package/dist/src/services/composition-watch.js +0 -79
- package/dist/src/services/elevenlabs.js +0 -222
- package/dist/src/services/file-directory.js +0 -117
- package/dist/src/services/fork-access.js +0 -93
- package/dist/src/services/fork-manifest.js +0 -42
- package/dist/src/services/ghostcut.js +0 -179
- package/dist/src/services/hyperframes.js +0 -3654
- package/dist/src/services/job-capacity.js +0 -14
- package/dist/src/services/job-logs.js +0 -197
- package/dist/src/services/jobs.js +0 -136
- package/dist/src/services/local-dynamo.js +0 -0
- package/dist/src/services/media-processing.js +0 -766
- package/dist/src/services/primitive-media-lambda.js +0 -280
- package/dist/src/services/providers.js +0 -2748
- package/dist/src/services/rate-limits.js +0 -262
- package/dist/src/services/scene-annotations.js +0 -32
- package/dist/src/services/serverless-auth.js +0 -382
- package/dist/src/services/serverless-jobs.js +0 -1084
- package/dist/src/services/serverless-provider-keys.js +0 -409
- package/dist/src/services/serverless-records.js +0 -1515
- package/dist/src/services/serverless-template-configs.js +0 -75
- package/dist/src/services/storage.js +0 -461
- package/dist/src/services/swipe-customize.js +0 -437
- package/dist/src/services/template-certification.js +0 -413
- package/dist/src/services/template-loader.js +0 -99
- package/dist/src/services/template-runtime-bundles.js +0 -217
- package/dist/src/services/template-sources.js +0 -1017
- package/dist/src/services/upstream.js +0 -248
- package/dist/src/services/video-normalization.js +0 -2
- package/dist/src/services/webhooks.js +0 -62
- package/dist/src/template-editor-pages.js +0 -2576
- package/dist/src/template-editor-shell.js +0 -2893
- package/dist/src/template-sdk.js +0 -4
- package/dist/src/worker.js +0 -17
- package/public/assets/discover-client-app.js +0 -1
- package/public/assets/file-directory-app.js +0 -3
- package/public/assets/homepage-app.js +0 -54
- package/public/assets/homepage-client-app.js +0 -80
- package/public/assets/page-runtime-client-app.js +0 -94
- package/public/assets/placeholders/scene-placeholder.png +0 -0
- package/src/assets/SELLING_AWARENESS_STAGES.md +0 -579
- package/src/assets/SELLING_WITH_HOOKS.md +0 -377
- package/src/assets/SELLING_WITH_VSLS.md +0 -606
- package/src/assets/favicon.ico +0 -0
- package/src/assets/logo-vidfarm.png +0 -0
|
@@ -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
|