@mevdragon/vidfarm-devcli 0.3.2 → 0.3.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/SKILL.director.md +9 -0
- package/SKILL.platform.md +11 -3
- package/package.json +1 -1
package/SKILL.director.md
CHANGED
|
@@ -142,6 +142,8 @@ Two modes:
|
|
|
142
142
|
- **`smart`** — uses the caller's Gemini/OpenAI/OpenRouter key. Samples frames, extracts scenes with labels and viral notes, extracts on-screen captions with positions, and detects viral DNA (hook, retention, payoff). Takes 30-60 seconds. Auto-fires GhostCut for subtitle removal in the background.
|
|
143
143
|
- **`time-slice`** — deterministic equal-duration split. Instant. No AI provider needed.
|
|
144
144
|
|
|
145
|
+
**Source length cap.** Vidfarm caps auto-decompose (and all inspiration ingest) at **120 seconds** of source video. If the source is longer, the API responds with `400 { ok: false, code: "source_too_long", duration_seconds, max_duration_seconds }` and no scenes are written. Trim the source before forking / ingesting.
|
|
146
|
+
|
|
145
147
|
Response:
|
|
146
148
|
|
|
147
149
|
```
|
|
@@ -150,6 +152,8 @@ Response:
|
|
|
150
152
|
|
|
151
153
|
If `ghostcut_pending: true`, poll `POST /api/v1/compositions/:forkId/ghostcut-poll` until status transitions to `done` or `failed`.
|
|
152
154
|
|
|
155
|
+
Read-only state (does **not** advance the job or bill): `GET /api/v1/compositions/:forkId/ghostcut`. Returns `{ status, task_id, original_source_url, mirrored_url, submitted_at_ms, completed_at_ms, failure_reason }`. Use this when you want to know both video URLs without triggering another poll — the AI copilot uses it to pick between the raw upload and the caption-free mirror before feeding a video into a primitive route (image extract, dedupe, trim, ai video edit, etc.). Prefer `mirrored_url` when `status === "done"` and the downstream call should be caption-free; prefer `original_source_url` when the user explicitly wants the raw upload or when `status !== "done"`.
|
|
156
|
+
|
|
153
157
|
## Publish
|
|
154
158
|
|
|
155
159
|
Publishing renders the fork's current working state to an MP4 using shared Remotion Lambda.
|
|
@@ -271,6 +275,11 @@ Flags: `--port`, `--dir`, `--host`, `--fork`, `--api-key` (or set `VIDFARM_API_K
|
|
|
271
275
|
|
|
272
276
|
The dev source is persisted in `localStorage.VIDFARM_DEV_SOURCE`, so you don't need to keep the `?dev=` param in the URL after the first load.
|
|
273
277
|
|
|
278
|
+
`dev-serve` (`vidfarm-devcli` 0.3.2+) is realtime-safe:
|
|
279
|
+
- Writes coming through `PUT /composition.html` / `PATCH /composition.json` **do not loop back** as reload events — the server suppresses filesystem notifications for the just-written file for 500ms so the editor's own save never triggers a self-reload.
|
|
280
|
+
- The SSE `hello` event carries an `instance` id + a monotonic `cursor`. If the client reconnects after a server restart the browser detects the new instance and fires a `vidfarm:dev-reload` so the editor resyncs from disk. A 20s heartbeat keeps the SSE connection alive through intermediate proxies.
|
|
281
|
+
- Port collisions surface as a clear `[dev-serve] port <n> is already in use` message instead of a raw stack trace. Request bodies are capped at 8MB and time out after 15s so a stuck client can't hang the server. File paths are canonicalised so `../etc/passwd`-style paths cannot escape `--dir`.
|
|
282
|
+
|
|
274
283
|
## What NOT to do
|
|
275
284
|
|
|
276
285
|
- 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.
|
package/SKILL.platform.md
CHANGED
|
@@ -161,6 +161,7 @@ All three files (`composition.html`, `composition.json`, `manifest.json`) are tr
|
|
|
161
161
|
- **Player adapter** — exposes `window.__player = { play(), pause(), seek(t), setTime(t), duration, playing, playbackRate, previewMuted, refreshClips() }` for the editor to control the iframe
|
|
162
162
|
- **Media coordination** — respects `data-playback-start`, `data-volume`, and clip-specific mute
|
|
163
163
|
- **Selection sync** — listens for `postMessage({ source: "hf-parent", ... })` from the parent editor to highlight/deselect clips
|
|
164
|
+
- **Origin-scoped messaging** — the runtime rejects inbound `message` events unless `event.source === window.parent`, so sibling iframes / popups cannot spoof play/pause/seek by forging `data.source`. Outbound `window.parent.postMessage(...)` calls are pinned to the parent's origin (seeded from `document.referrer` and refreshed from the first accepted control message) instead of `"*"`, so preview frame coordinates and layer ids no longer leak to any embedding third-party page.
|
|
164
165
|
|
|
165
166
|
The same runtime is used inside the Trackpad Editor iframe AND when a published fork is embedded elsewhere. It is agnostic to whether it's being edited or viewed.
|
|
166
167
|
|
|
@@ -201,6 +202,8 @@ The Remotion Lambda is **shared across all templates and forks** — it's an inf
|
|
|
201
202
|
|
|
202
203
|
Both modes update the composition.html and composition.json in place, and create a fork version snapshot with `reason: "manual"`.
|
|
203
204
|
|
|
205
|
+
**Source-duration guardrail.** `probeMediaAsset()` in `src/services/media-processing.ts` enforces a hard cap of `MAX_PROBE_DURATION_SECONDS` (120s). If the probed source exceeds it, the probe throws `MediaDurationExceededError` and the route responds with `400 { code: "source_too_long", duration_seconds, max_duration_seconds }`. The same cap applies to inspiration ingest (`ensureInspirationReadyThenFork`) — over-long submissions land as `InspirationRecord { status: "failed", errorMessage }` without ever creating a Template or fork. This is baked into the probe (not the route) so every consumer — auto-decompose, ingest, `video_probe` / `audio_probe` primitives — is protected from repeated probes of long videos being used to burn Lambda / ffmpeg / vision-API budget.
|
|
206
|
+
|
|
204
207
|
## GhostCut integration
|
|
205
208
|
|
|
206
209
|
GhostCut is an external AI subtitle-removal service. Vidfarm proxies to it via `src/services/ghostcut.ts`:
|
|
@@ -211,6 +214,8 @@ GhostCut is an external AI subtitle-removal service. Vidfarm proxies to it via `
|
|
|
211
214
|
|
|
212
215
|
State is persisted per fork at `compositions/forks/<forkId>/working/ghostcut.json`. The client polls `POST /api/v1/compositions/:forkId/ghostcut-poll` every few seconds during smart decompose.
|
|
213
216
|
|
|
217
|
+
Read-only companion: `GET /api/v1/compositions/:forkId/ghostcut` returns `{ status, task_id, original_source_url, mirrored_url, submitted_at_ms, completed_at_ms, failure_reason }` without touching GhostCut. Non-billing. Exposed for two reasons: (1) the editor chat harness / AI copilot uses it to look up both video URLs on demand so downstream primitive calls can pick raw-upload vs caption-free-mirror without hardcoding either in chat context; (2) it's `http_request`-allowlisted under `COMPOSITIONS_PREFIX` in `src/app.ts`, which is why the editor chat system prompt (`src/editor-chat.ts`) directs the model to call it before feeding a video into a primitive route. The AI is instructed **not** to call `POST /ghostcut-poll` unless the user explicitly asks to advance subtitle removal.
|
|
218
|
+
|
|
214
219
|
Configuration: `GHOSTCUT_KEY` and `GHOSTCUT_SECRET` env vars per stage. Pass-through cost: `GHOSTCUT_USD_PER_30S` (default 0.10).
|
|
215
220
|
|
|
216
221
|
## Editor Chat
|
|
@@ -363,10 +368,13 @@ vidfarm-devcli dev-serve --dir ./my-fork --port 4321
|
|
|
363
368
|
|
|
364
369
|
Edit flags: `--port`, `--dir`, `--host`, `--fork`, `--api-key` (or `VIDFARM_API_KEY`), `--share`, `--refetch`.
|
|
365
370
|
|
|
366
|
-
`dev-serve` behavior:
|
|
371
|
+
`dev-serve` behavior (`vidfarm-devcli` 0.3.2+):
|
|
367
372
|
- Serves `composition.html` / `composition.json` / `manifest.json` (and `/versions/{n}/…`) with CORS `*`.
|
|
368
373
|
- Accepts `PUT /composition.html` and `PATCH /composition.json`.
|
|
369
|
-
- Watches the dir with `fs.watch`, coalesces bursts, fans out reload events via `GET /_events` SSE.
|
|
374
|
+
- Watches the dir with `fs.watch`, coalesces bursts (80ms), fans out reload events via `GET /_events` SSE.
|
|
375
|
+
- Reload loop prevention: any successful `PUT` / `PATCH` marks the target file "self-write suppressed" for 500ms so the fs.watch event fired by the write we just performed does NOT round-trip back to the editor as a reload event.
|
|
376
|
+
- SSE `hello` includes `{ at, instance, cursor }`. `instance` changes on every dev-serve restart so a reconnecting client can detect a restart and force a resync; `cursor` is a monotonic counter that bumps on every broadcast reload. Heartbeat `: heartbeat <ts>` every 20s keeps intermediate proxies from timing out the connection.
|
|
377
|
+
- Safety rails: request bodies capped at 8MB with a 15s body-read timeout; path resolution runs through `path.relative(rootDir, ...)` so `versions/../../etc/passwd`-style paths cannot escape the served directory; `EADDRINUSE` produces a plain-English error instead of a raw stack; broadcast iterates a snapshot of the client set so a mid-broadcast disconnect can't skip healthy clients; `SIGINT` + `SIGTERM` both close cleanly with a 2s hard-exit fallback.
|
|
370
378
|
|
|
371
|
-
The editor side lives in `demo/src/dev-mode.ts` — a self-installing module imported at the top of `demo/src/main.tsx`. It reads `?dev=<url>` (persisted to `localStorage.VIDFARM_DEV_SOURCE`), monkey-patches `window.fetch` to rewrite `/api/v1/compositions/{forkId}/*` → `${devSource}/*`, injects a persistent orange banner with an Exit button, and dispatches `vidfarm:dev-reload` on SSE reload events. `HyperframesStudioEditor.tsx` listens for that event and re-runs the composition fetch effect with `cache: "reload"`.
|
|
379
|
+
The editor side lives in `demo/src/dev-mode.ts` — a self-installing module imported at the top of `demo/src/main.tsx`. It reads `?dev=<url>` (persisted to `localStorage.VIDFARM_DEV_SOURCE`), monkey-patches `window.fetch` to rewrite `/api/v1/compositions/{forkId}/*` → `${devSource}/*`, injects a persistent orange banner with an Exit button, and dispatches `vidfarm:dev-reload` on SSE reload events. It also caches the `hello.instance` and re-dispatches `vidfarm:dev-reload` with `{ reason: "server-restart" }` when a reconnect sees a different instance. `HyperframesStudioEditor.tsx` listens for that event and re-runs the composition fetch effect with `cache: "reload"`.
|
|
372
380
|
- Lambdas — `infra/lambda/*.ts`
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mevdragon/vidfarm-devcli",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.3",
|
|
4
4
|
"description": "Local bridge for the Vidfarm Trackpad Editor. Point it at a template id, edit composition.html on disk (Claude Code, Codex, etc.), preview live in the browser at https://vidfarm.cc/editor/.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|