@amg555/frameforge-mcp 0.0.0-stage → 2.3.0
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/.bob/artifacts/enterprise-ai-video-agent-platform-agnostic-implementation-guide.html +464 -0
- package/.claude-plugin/marketplace.json +17 -0
- package/.gitattributes +19 -0
- package/.mcp.json +45 -0
- package/CHANGELOG.md +431 -0
- package/CLAUDE.md +278 -0
- package/CONTRIBUTING.md +112 -0
- package/LICENSE +21 -0
- package/Procfile +1 -0
- package/README.md +359 -2
- package/SECURITY.md +71 -0
- package/START_HERE.md +144 -0
- package/assets/metadata/assets.json +5 -0
- package/assets/metadata/embeddings.json +5 -0
- package/install.sh +64 -0
- package/package.json +58 -4
- package/src/animation/animation-system.js +229 -0
- package/src/asset-library/README.md +188 -0
- package/src/asset-library/asset-library.js +639 -0
- package/src/audio-mixer/mixer.js +187 -0
- package/src/cli/doctor.js +173 -0
- package/src/cli/make.js +655 -0
- package/src/cli/setup-keys.js +378 -0
- package/src/config/load-config.js +72 -0
- package/src/fact-checker/checker.js +384 -0
- package/src/fact-checker/review-gate.js +306 -0
- package/src/fact-checker/validation-set.js +1078 -0
- package/src/mcp-server/server.js +1299 -0
- package/src/mcp-server/stdio.js +87 -0
- package/src/pipeline/artifact-manifest.js +323 -0
- package/src/pipeline/artifact-retention.js +189 -0
- package/src/pipeline/external-adapter.js +366 -0
- package/src/pipeline/providers.js +853 -0
- package/src/pipeline/retry-policy.js +134 -0
- package/src/pipeline/state-machine.js +297 -0
- package/src/pipeline/telemetry.js +162 -0
- package/src/quality-gate/gate.js +229 -0
- package/src/schema/scene.schema.json +101 -0
- package/src/shorts-extractor/extractor.js +343 -0
- package/src/utils/log-redaction.js +274 -0
- package/src/video-renderer/renderer.js +364 -0
- package/src/worker/server-render-free.js +420 -0
- package/src/worker/server.js +328 -0
- package/src/workflows/assets.js +131 -0
- package/src/workflows/ingest.js +299 -0
- package/src/workflows/packaging.js +156 -0
- package/src/workflows/publishing.js +206 -0
- package/src/workflows/render.js +102 -0
- package/src/workflows/research.js +151 -0
- package/src/workflows/script.js +210 -0
- package/src/workflows/shorts.js +113 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,431 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to FrameForge are documented here.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## [2.3.0] — 2026-11-02
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
|
|
11
|
+
- **HTML renderer** — complete Playwright-based HTML compositor for motion-graphics-quality video output. Activated via the `html-renderer` skill (`plugins/video-pipeline/skills/html-renderer/SKILL.md`).
|
|
12
|
+
- `plugins/video-pipeline/skills/video-pipeline/assets/project/reel/engine.js` — full `seek(t)` runtime: `layer()`, `text()`, `chip()`, `wallCard()`, `imageCard()`, `stamp()`, `codeCard()`, `terminal()`, `at()`, `stagger()`, `captions()`, `cue()`, `faceAt()`, `spring()`, `lerp()`, `easeIn/Out/InOut()`, browser preview loop
|
|
13
|
+
- `reel/engine.css` — base reset + design tokens
|
|
14
|
+
- `scripts/render.mjs` — Playwright frame renderer (sheet / stills / full / mux)
|
|
15
|
+
- `scripts/mix_audio.py` — voice concat + sidechain ducking + -14 LUFS
|
|
16
|
+
- `scripts/verify.py` — 7-gate quality verification (same gates as `QualityGate`)
|
|
17
|
+
- `scripts/transcribe.py` — Whisper word-level caption outline generator (`openai-whisper`, free local)
|
|
18
|
+
- `scripts/capture.mjs` — headless Playwright screenshot for b-roll (site, tweet, URL)
|
|
19
|
+
- `scripts/stock.py` — Pexels stock footage/image downloader (free API key); Pollinations fallback for images
|
|
20
|
+
- `scripts/html-renderer/new-project.mjs` updated: copies real engine files instead of writing stubs; includes all 8 scripts
|
|
21
|
+
- **Tool 14: `get_job_status`** — MCP tool to poll a job queued by `queue_full_pipeline`. Returns `{ status, stage, result, error }`. Added to `src/mcp-server/server.js`; automatically available on stdio transport. 5 new tests.
|
|
22
|
+
- **Cloudflare KV rate limiter** — sliding-window 100 req/hr per bearer token. SHA-256-hashed key stored in `RATE_LIMITS` KV namespace. Returns HTTP 429 + `Retry-After` when exceeded. KV binding documented in `cloudflare-mcp/wrangler.toml`. Rate-limiting skipped when KV not bound (local dev).
|
|
23
|
+
|
|
24
|
+
### Changed
|
|
25
|
+
|
|
26
|
+
- `VideoRenderer.assembleMain` — minimum scene guard lowered from 5 → 1 (throws only on 0; warns below 3). The README's 2-scene `create_video_from_script` examples now work without error.
|
|
27
|
+
- Cloudflare Worker version → `2.3.0`; `SERVER_INFO.version` → `2.3.0`; `package.json` → `2.3.0`
|
|
28
|
+
- `scripts/deep-validate.js` — +27 checks (engine asset files, rate-limit, Tool 14, new scripts, auth token forwarding, publish_video invariants)
|
|
29
|
+
- `README.md` — version badge, test count, Tool 14 row, HTML Renderer section, architecture diagram tool counts updated
|
|
30
|
+
|
|
31
|
+
### Security
|
|
32
|
+
|
|
33
|
+
- `get_job_status` and `queue_full_pipeline` now forward `INTERNAL_API_TOKEN` as `Authorization: Bearer` in all HTTP requests to the worker API. Previously these two tools made unauthenticated calls.
|
|
34
|
+
- `publish_video` manifest no longer emits stale `youtubeTitle`/`youtubeDescription` alias keys.
|
|
35
|
+
- `cloudflare-mcp/src/index.js` architecture comment corrected (was "Telegram / YouTube").
|
|
36
|
+
- `log-redaction.js` — removed stale YouTube OAuth2 key names (`youtubeRefreshToken`, `youtubeClientSecret`).
|
|
37
|
+
- MCP HTTP server now enforces `MCP_AUTH_TOKEN` when set, using `crypto.timingSafeEqual` to prevent timing oracle attacks. Previously the token was validated only in the Cloudflare Worker proxy; the local HTTP server only logged a warning.
|
|
38
|
+
- `external-adapter.js` — fixed `this`-binding bug in `downloadOutput()` where `this._authHeader()` was called inside a nested regular function that lost the instance context. Auth headers are now captured before the closure.
|
|
39
|
+
- `providers.js` — removed unused `execSync` import (imported but never called; reduces unnecessary attack surface).
|
|
40
|
+
- `publishing.js` — removed stale `youtubeTitle`/`youtubeDescription` fallback from title/description extraction.
|
|
41
|
+
- Server startup no longer logs active provider configuration (`LLM=groq TTS=edge-tts ...`) to stdout, preventing accidental info disclosure in log aggregation pipelines.
|
|
42
|
+
- `scripts/deep-validate.js` — +5 new invariant checks covering MCP auth enforcement, provider config log hygiene, and stale YouTube fallback removal.
|
|
43
|
+
|
|
44
|
+
### Tests
|
|
45
|
+
|
|
46
|
+
- 456 tests, 98 suites (was 449 / 96 before this patch)
|
|
47
|
+
|
|
48
|
+
---
|
|
49
|
+
|
|
50
|
+
## [2.2.0] — 2026-11-02
|
|
51
|
+
|
|
52
|
+
### Changed
|
|
53
|
+
|
|
54
|
+
- **Delivery channels** — Telegram and YouTube removed from `publishing.js`. Replaced with two platform-agnostic channels:
|
|
55
|
+
- `file` — copy master + shorts to `N8N_FILES_DIR/published/` (default)
|
|
56
|
+
- `webhook` — POST JSON delivery summary to `PUBLISH_WEBHOOK_URL`
|
|
57
|
+
- **Review notifications** — `review-gate.js` no longer uses Telegram `sendMessage`. Now POSTs structured JSON events (`HUMAN_REVIEW_REQUIRED`, `PRODUCTION_REJECTED`) to `REVIEW_NOTIFY_URL` — compatible with any HTTP receiver (n8n, Make, Zapier, custom)
|
|
58
|
+
- **Tool 13 renamed** — `n8n_webhook_trigger` → `queue_full_pipeline`. POSTs `{ type: "full_pipeline", payload }` to `WORKER_API_URL/jobs`. All three server variants updated (HTTP, stdio, Cloudflare Worker)
|
|
59
|
+
- **`src/cli/make.js`** — `--channels` default changed from `telegram` to `file`; Telegram batch-summary curl block removed
|
|
60
|
+
- **`src/cli/setup-keys.js`** — Telegram + YouTube setup sections replaced with `PUBLISH_WEBHOOK_URL` and `REVIEW_NOTIFY_URL` sections
|
|
61
|
+
- **`docs/env.template`** — Telegram and YouTube env vars removed; `PUBLISH_WEBHOOK_URL`, `REVIEW_NOTIFY_URL`, `WORKER_API_URL` added
|
|
62
|
+
- **`scripts/deep-validate.js`** — stale YouTube OAuth2 + Telegram + n8n checks replaced with file/webhook delivery checks; `SERVER_INFO version` updated to `2.2.0`
|
|
63
|
+
- **`package.json`** — version bumped to `2.2.0`
|
|
64
|
+
- **`README.md`** — delivery description, Tool 13 name, test counts, badge versions updated
|
|
65
|
+
|
|
66
|
+
### Removed
|
|
67
|
+
|
|
68
|
+
- **`docs/youtube-upload.md`** — YouTube OAuth2 upload guide. YouTube delivery removed from the core pipeline; external delivery via `external_platform_call` or a webhook receiver handles it.
|
|
69
|
+
- **`TELEGRAM_BOT_TOKEN`**, **`TELEGRAM_CHAT_ID`**, **`YOUTUBE_CLIENT_ID`**, **`YOUTUBE_CLIENT_SECRET`**, **`YOUTUBE_REFRESH_TOKEN`**, **`N8N_WEBHOOK_URL`** — all env vars no longer read by any source file.
|
|
70
|
+
|
|
71
|
+
### New env vars
|
|
72
|
+
|
|
73
|
+
| Var | Purpose |
|
|
74
|
+
|-----|---------|
|
|
75
|
+
| `PUBLISH_WEBHOOK_URL` | HTTP endpoint that receives delivery summary JSON on publish |
|
|
76
|
+
| `REVIEW_NOTIFY_URL` | HTTP endpoint that receives fact-check review request events |
|
|
77
|
+
| `WORKER_API_URL` | Worker API base URL for `queue_full_pipeline` (default: `http://localhost:3211`) |
|
|
78
|
+
|
|
79
|
+
---
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
## [2.1.0] — 2026-11-02
|
|
83
|
+
|
|
84
|
+
### Added
|
|
85
|
+
|
|
86
|
+
- **5 new test modules** — 103 new tests across previously untested production modules:
|
|
87
|
+
- `src/quality-gate/gate.test.js` — QualityGate result schema, check entry shape, _ffprobe/_ffmpeg helpers
|
|
88
|
+
- `src/shorts-extractor/extractor.test.js` — _focalToFFmpegX (even-pixel constraint, clamping), _scoreSegment, _selectSegments (non-overlap, ≤5), _silenceToBoundaries, _defaultFocalY, error on too-short video
|
|
89
|
+
- `src/audio-mixer/mixer.test.js` — constructor, detectPauses graceful fallback, mixWithDucking/mixVideo error paths
|
|
90
|
+
- `src/video-renderer/renderer.test.js` — _captionFilter (sanitisation, truncation, upper-case), _discoverScenes (ordering, size filter, sceneCount cap), _findRandomMusicFile, renderScene/assembleMain error paths
|
|
91
|
+
- `src/fact-checker/checker.test.js` — _normalizeClaim, _classifyRisk, _requiredSources, _hash, _gatePass, _domainGroup, checkClaims (cache, structure, empty), checkClaim, formatCitation
|
|
92
|
+
|
|
93
|
+
### Changed
|
|
94
|
+
|
|
95
|
+
- **`src/schema/scene.schema.json`** — aligned with the v2.0.0 breaking changes shipped in that release:
|
|
96
|
+
- `schema_version` default corrected from `1.0.0` → `2.0.0`
|
|
97
|
+
- `character_id` enum (`ancient_man|modern_man|none`) replaced with a free-form string description
|
|
98
|
+
- `character` block no longer `required` in schema `required` array (character is optional)
|
|
99
|
+
- `narration` and `visual_description` `minLength` relaxed from `10` → `1`
|
|
100
|
+
- `camera_hint` enum expanded to include all 11 AnimationSystem hints: `slow_zoom_in`, `slow_zoom_out`, `tilt_up`, `tilt_down`, `ken_burns` (were missing since the animation system was extended)
|
|
101
|
+
- `character.pose` and `character.emotion` enums removed — free-form strings
|
|
102
|
+
- **`package.json`** — version bumped to `2.1.0`; `test` script extended to include 4 new test files; dead `check-credentials` script reference to deleted `n8n-log-redaction.js` fixed
|
|
103
|
+
|
|
104
|
+
### Removed
|
|
105
|
+
|
|
106
|
+
- **`src/utils/n8n-log-redaction.js`** — n8n Code node utility. n8n was removed from this project; the file was dead code. Log redaction is handled by `src/utils/log-redaction.js`.
|
|
107
|
+
- **`src/asset-library/test-asset-library.js`** — legacy console-log test script referencing the removed `ancient_man`/`modern_man` characters. Not in node:test format, not run by `npm test`.
|
|
108
|
+
|
|
109
|
+
---
|
|
110
|
+
|
|
111
|
+
## [2.0.1] — 2026-11-02
|
|
112
|
+
|
|
113
|
+
### Security
|
|
114
|
+
|
|
115
|
+
- **Timing-safe token comparison** — `cloudflare-mcp` and `server-render-free.js` both used plain `===` to compare auth tokens. Replaced with `crypto.subtle.timingSafeEqual` (Worker) and `crypto.timingSafeEqual` (Node) to eliminate token-oracle timing attacks.
|
|
116
|
+
- **Request body size limits** — All three HTTP servers (`server.js`, `server-render-free.js`, `src/mcp-server/server.js`) now reject bodies > 4 MB with HTTP 413, preventing OOM from oversized payloads.
|
|
117
|
+
- **Path traversal guards** — `edit_scenes` and `render_from_scenes` now reject any `productionId` containing `/` or `\`. `multi_voice_tts` now validates `outputPath` is inside `FILES_DIR`.
|
|
118
|
+
- **`multi_voice_tts` env-mutation race fixed** — The tool previously mutated `process.env` and restored it in `finally`, which was a race condition under concurrent MCP calls. `TTSProvider` now accepts per-call constructor options `{platform, voice, rate, apiKey, webhookUrl}` that override `process.env` without mutation. All `_edgeTTS`/`_elevenLabs`/`_azureTTS`/`_openaiTTS`/`_customTTS` methods read `this._overrides` first.
|
|
119
|
+
- **Startup warnings** — Both `server.js` and `server-render-free.js` now print a `WARNING` on startup when `MCP_AUTH_TOKEN` / `INTERNAL_API_TOKEN` is not set.
|
|
120
|
+
- **Health endpoint hardened** — `src/mcp-server/server.js` health check no longer leaks the full tool list.
|
|
121
|
+
|
|
122
|
+
### Fixed
|
|
123
|
+
|
|
124
|
+
- **Stale `youtubeTitle` field names** — `runFullPipeline` in both `server-render-free.js` and `src/worker/server.js` was passing `scr.youtubeTitle` / `scr.youtubeDescription` / `scr.youtubeTags` to `runPackaging`. Script schema v2.0.0 renamed these fields to `title` / `description` / `tags`. The pipeline now uses the correct names.
|
|
125
|
+
- **Cloudflare Worker tool list was stale** — `cloudflare-mcp/src/index.js` only advertised 6 tools. All 13 tools are now exposed (`ingest_media`, `edit_scenes`, `render_from_scenes`, `external_platform_call`, `create_video_from_script`, `multi_voice_tts`, `n8n_webhook_trigger` added).
|
|
126
|
+
- **`write_script` description updated** — Removed "55-scene" and "educational comedy" from the tool description (stale since v2.0.0).
|
|
127
|
+
- **Cloudflare Worker `serverInfo.version` updated** to `2.0.0` (was `1.1.0`).
|
|
128
|
+
- **`SERVER_INFO.version` in `server.js`** updated to `2.0.0` (was `1.0.0`).
|
|
129
|
+
|
|
130
|
+
---
|
|
131
|
+
|
|
132
|
+
## [2.0.0] — 2026-11-02
|
|
133
|
+
|
|
134
|
+
### Breaking Changes
|
|
135
|
+
|
|
136
|
+
**Scene schema updated to `2.0.0`.**
|
|
137
|
+
- `character_id` is now a free-form string (was an enum of `ancient_man|modern_man|none`)
|
|
138
|
+
- `visual_description` no longer requires `[CAMP][MODERN][COMPARE][REACTION][TIMELINE]` tags
|
|
139
|
+
- `youtubeTitle`/`youtubeDescription`/`youtubeTags` renamed to `title`/`description`/`tags` in `runScript()` return value (legacy keys still returned for backwards compat)
|
|
140
|
+
|
|
141
|
+
### Removed
|
|
142
|
+
- All hardcoded domain content: "YouTube history researcher", "ancient_man", "modern_man", caveman loincloth image style constant, `[CAMP][MODERN][COMPARE][REACTION][TIMELINE]` scene tags
|
|
143
|
+
- `.kiro/` folder removed from git history entirely (IBM Kiro IDE dev artifacts — not needed by contributors)
|
|
144
|
+
- Hardcoded 55-scene / 1000-word minimums relaxed to 5 scenes / 50 words (short-form reels now work)
|
|
145
|
+
|
|
146
|
+
### Added
|
|
147
|
+
|
|
148
|
+
**Tool 11: `create_video_from_script`** — Agent-first path. The IDE agent writes the complete `scenes[]` array in its own context; FrameForge renders without any internal LLM calls. This is the primary path for , Claude Code, Cursor, and any agentic IDE.
|
|
149
|
+
|
|
150
|
+
```json
|
|
151
|
+
{ "scenes": [
|
|
152
|
+
{ "narration": "Spoken text", "caption": "OVERLAY", "visual_description": "Image prompt" }
|
|
153
|
+
] }
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
**Tool 12: `multi_voice_tts`** — Per-call TTS platform override. Call ElevenLabs, Azure, OpenAI TTS, edge-tts, or any webhook without changing global `.env`. Pass `apiKey` inline for voice cloning workflows.
|
|
157
|
+
|
|
158
|
+
**Generic `research_topic` and `write_script` tools** — now accept:
|
|
159
|
+
- `persona`: who the narrator is (e.g. "travel blogger", "tech reviewer")
|
|
160
|
+
- `format`: video format (e.g. "Instagram reel", "long-form tutorial", "explainer")
|
|
161
|
+
- `style`: content style (e.g. "comedic", "documentary", "educational", "vlog")
|
|
162
|
+
- `language`: BCP-47 code for the script language
|
|
163
|
+
- `targetDuration`: video duration in seconds (auto-calculates scene count)
|
|
164
|
+
- `imageStyle`: visual aesthetic for AI image prompts ("photorealistic", "anime", "watercolour", etc.)
|
|
165
|
+
|
|
166
|
+
**`generate_scene_image`** — `imageStyle` param replaces hardcoded art style. Any visual aesthetic now possible.
|
|
167
|
+
|
|
168
|
+
### How FrameForge works with an agentic IDE
|
|
169
|
+
|
|
170
|
+
FrameForge is a **rendering execution engine**. Your IDE ( / Claude Code / Cursor) is the intelligence.
|
|
171
|
+
|
|
172
|
+
**Farhan's workflow** (the PDF): Claude Desktop controls a browser, types scripts into ElevenLabs, runs Remotion renders. The agent drives the GUI.
|
|
173
|
+
|
|
174
|
+
**FrameForge + agentic IDE**: The IDE writes scenes in its context and calls structured MCP tool functions. No browser control needed. The IDE calls `create_video_from_script` → FrameForge handles TTS + image gen + FFmpeg assembly → returns `masterPath`. Like Opus Clip but controlled by your IDE.
|
|
175
|
+
|
|
176
|
+
---
|
|
177
|
+
|
|
178
|
+
## [1.4.1] — 2026-11-02
|
|
179
|
+
|
|
180
|
+
### Changed — LLM Provider Overhaul
|
|
181
|
+
|
|
182
|
+
**Removed**: `watsonx` provider. All WatsonX references removed from providers, doctor, setup wizard, and env template.
|
|
183
|
+
|
|
184
|
+
**Added 6 new LLM providers** (all OpenAI-compatible except Anthropic):
|
|
185
|
+
|
|
186
|
+
| Provider | Default model | Key env var | Free tier |
|
|
187
|
+
|---|---|---|---|
|
|
188
|
+
| `cerebras` | `llama3.3-70b` (2000+ tok/s) | `CEREBRAS_API_KEY` | ✅ |
|
|
189
|
+
| `openrouter` | `anthropic/claude-sonnet-4-5` | `OPENROUTER_API_KEY` | ✅ credits |
|
|
190
|
+
| `anthropic` | `claude-sonnet-4-5` | `ANTHROPIC_API_KEY` | — |
|
|
191
|
+
| `gemini` | `gemini-2.0-flash` | `GEMINI_API_KEY` | ✅ |
|
|
192
|
+
| `mistral` | `mistral-large-latest` | `MISTRAL_API_KEY` | — |
|
|
193
|
+
| `together` | `meta-llama/Llama-3.3-70B-Instruct-Turbo` | `TOGETHER_API_KEY` | ✅ credits |
|
|
194
|
+
|
|
195
|
+
- `openai` default model updated from `gpt-4o-mini` → `gpt-4o`
|
|
196
|
+
- `groq` default updated to `llama-3.3-70b-versatile`
|
|
197
|
+
- **Anthropic native API**: Anthropic uses `/v1/messages` (not `/chat/completions`) — a private `_anthropicChat()` shim handles this transparently.
|
|
198
|
+
- **OpenRouter headers**: `HTTP-Referer` + `X-Title` attribution headers added automatically.
|
|
199
|
+
- **`LLM_MODEL` env var** overrides the default model for any provider.
|
|
200
|
+
- **`webhook` provider** now explicitly listed in `LLM_DEFAULTS` (covers LM Studio, vLLM, LocalAI, any proxy).
|
|
201
|
+
- **Model update guide** embedded as a comment in `providers.js` with docs URL for each provider.
|
|
202
|
+
|
|
203
|
+
**`npm run doctor`** updated — detects all configured providers, shows active model name, recommends free options with a better fix message.
|
|
204
|
+
|
|
205
|
+
**`npm run setup` wizard** updated — all 6 new providers in the menu, WatsonX removed.
|
|
206
|
+
|
|
207
|
+
**`.kiro/config/production-secrets.env.template`** — full rewrite:
|
|
208
|
+
- All LLM providers documented with inline links
|
|
209
|
+
- External platform adapter section added
|
|
210
|
+
- YouTube OAuth2 section added
|
|
211
|
+
- TTS key names corrected (`AZURE_SPEECH_KEY` was `AZURE_TTS_KEY`)
|
|
212
|
+
|
|
213
|
+
---
|
|
214
|
+
|
|
215
|
+
## [1.4.0] — 2026-11-02
|
|
216
|
+
|
|
217
|
+
### Added — Flexible Pipeline Modes
|
|
218
|
+
|
|
219
|
+
**Pipeline mode system (`--mode` flag)**
|
|
220
|
+
FrameForge is no longer all-or-nothing. Every stage is now independently skippable.
|
|
221
|
+
|
|
222
|
+
| Mode | What it does |
|
|
223
|
+
|---|---|
|
|
224
|
+
| `full-auto` (default) | Research → Script → AI Images → TTS → Render → Publish |
|
|
225
|
+
| `edit-only` | Skip all AI generation. Supply your own clips/images/audio. |
|
|
226
|
+
| `script-only` | Research + Script only. Review/edit the JSON, then resume. |
|
|
227
|
+
| `custom` | Mix and match with `--no-*` flags. |
|
|
228
|
+
|
|
229
|
+
**New CLI flags**
|
|
230
|
+
- `--mode full-auto|edit-only|script-only|custom`
|
|
231
|
+
- `--scenes-file <path>` — Load a scenes JSON directly, skip research + script
|
|
232
|
+
- `--script-file <path>` — Load an approved script JSON, skip research only
|
|
233
|
+
- `--media-dir <path>` — Auto-discover video/image/audio clips from a directory
|
|
234
|
+
- `--no-research` — Use `--topic` as raw context instead of running the research stage
|
|
235
|
+
- `--no-script` — Skip script generation (requires `--scenes-file`)
|
|
236
|
+
- `--no-images` — Skip AI image generation (use `video_path`/`image_path` in scenes)
|
|
237
|
+
- `--no-tts` — Skip TTS synthesis (use `audio_path` in scenes)
|
|
238
|
+
- `--no-publish` — Render only, skip all delivery channels
|
|
239
|
+
- `--dry-run` now prints a resume hint: `npm run make -- --script-file <path>`
|
|
240
|
+
|
|
241
|
+
**Interactive key setup wizard (`src/cli/setup-keys.js`)**
|
|
242
|
+
Walk through every provider in ~2 minutes. Writes a ready-to-use `.env` file.
|
|
243
|
+
- `npm run setup` — Launch the wizard
|
|
244
|
+
- `npm run setup:check` — Validate existing `.env`
|
|
245
|
+
- `node src/cli/setup-keys.js --out .env.production` — Write to a named file
|
|
246
|
+
- Covers: Groq / OpenAI / WatsonX / Ollama / webhook LLM, edge-tts / ElevenLabs (voice cloning) / Azure / OpenAI TTS / webhook TTS, Pollinations / fal.ai / Replicate / DALL-E / webhook image, Telegram, YouTube OAuth2, external platform adapter
|
|
247
|
+
|
|
248
|
+
**Media ingest workflow (`src/workflows/ingest.js`)**
|
|
249
|
+
Build a production-ready `scenes.json` from user-supplied media — no LLM needed.
|
|
250
|
+
- File-based: `--scenes-file ./my-scenes.json`
|
|
251
|
+
- Directory auto-discovery: `--media-dir ./clips` (pairs `scene01.mp4` + `scene01.mp3` + `scene01.txt` automatically)
|
|
252
|
+
- Scenes format: `{ narration, caption, video_path, image_path, audio_path, camera_hint, music_mood }`
|
|
253
|
+
- Reports how many scenes need AI images vs pre-supplied, TTS vs pre-recorded audio
|
|
254
|
+
|
|
255
|
+
**External platform adapter (`src/pipeline/external-adapter.js`)**
|
|
256
|
+
Connect FrameForge to any external platform via HTTP — VoxForge, ElevenLabs, Remotion, Runway, Pika, n8n, Make, or any REST API.
|
|
257
|
+
- Configure with 5 env vars: `EXTERNAL_PLATFORM_NAME`, `EXTERNAL_PLATFORM_URL`, `EXTERNAL_PLATFORM_KEY`, `EXTERNAL_PLATFORM_INGEST_PATH`, `EXTERNAL_PLATFORM_RENDER_PATH`
|
|
258
|
+
- Built-in presets for: `elevenlabs`, `remotion`, `voxforge`, `runway`, `pika`
|
|
259
|
+
- Operations: `ingestMedia()`, `renderScenes()`, `checkStatus()`, `downloadOutput()`
|
|
260
|
+
- Polls job status automatically until completion
|
|
261
|
+
|
|
262
|
+
**4 new MCP tools** (accessible from Cursor, Claude Code, Kiro, Windsurf, )
|
|
263
|
+
- `ingest_media` — Upload clips/images/audio, get a scenes manifest back
|
|
264
|
+
- `edit_scenes` — Read and patch a scenes manifest (update captions, swap files, append scenes)
|
|
265
|
+
- `render_from_scenes` — Render directly from a manifest, skipping research/script
|
|
266
|
+
- `external_platform_call` — Bridge to VoxForge/Runway/ElevenLabs/Remotion/any platform
|
|
267
|
+
|
|
268
|
+
**`assets.js` `skipImages`/`skipTts` flags**
|
|
269
|
+
`runAssets({ skipImages: true, skipTts: true })` now skips generation stages cleanly. Scenes with `_skipImageGen` or `_skipTts` fields (set by `--no-images` / `--no-tts`) are respected.
|
|
270
|
+
|
|
271
|
+
**New npm scripts**
|
|
272
|
+
- `npm run setup` — Key setup wizard
|
|
273
|
+
- `npm run setup:check` — Validate `.env`
|
|
274
|
+
- New `frameforge-setup` CLI binary
|
|
275
|
+
|
|
276
|
+
---
|
|
277
|
+
|
|
278
|
+
## [1.3.2] — 2026-11-02
|
|
279
|
+
|
|
280
|
+
### Fixed
|
|
281
|
+
|
|
282
|
+
- **`asset-library.js` `findAsset` — null-dereference on semantic match** (#bug): `_semanticSearch`
|
|
283
|
+
returns the asset directly, not a `{similarity, asset}` wrapper. The caller checked
|
|
284
|
+
`semanticMatch.similarity` which was always `undefined`, so semantic fallback never matched.
|
|
285
|
+
The guard is now `if (semanticMatch)` with a direct return.
|
|
286
|
+
- **`asset-library.js` `_callPollinationsAPI` — double download** (#bug): The method called
|
|
287
|
+
`_downloadAsset(imageUrl, description)` internally, then `generateAsset` also called
|
|
288
|
+
`_downloadAsset` with the returned path. The image was fetched twice. Fixed: `_callPollinationsAPI`
|
|
289
|
+
now returns the URL string only; `generateAsset → _downloadAsset` performs the single download.
|
|
290
|
+
- **`asset-library.js` `_generateAssetId` — timestamp-based ID breaks idempotency** (#bug):
|
|
291
|
+
`Date.now()` was part of every generated ID, so running the same description twice created
|
|
292
|
+
two registry entries for the same asset. ID is now a deterministic content hash of the
|
|
293
|
+
visual description.
|
|
294
|
+
- **`asset-library.js` `_saveRegistry` — non-atomic write corrupts JSON under concurrency** (#bug):
|
|
295
|
+
A crash mid-write left a truncated `assets.json`. Registry is now written to a `.tmp` file
|
|
296
|
+
and atomically renamed, so readers always see a complete file.
|
|
297
|
+
- **`pipeline/providers.js` `_download` — no redirect following** (#bug): Pollinations and most
|
|
298
|
+
image CDNs issue 302 redirects. The old helper rejected non-200 responses, making every
|
|
299
|
+
Pollinations download fail. The method now follows up to 5 redirects and cleans up the
|
|
300
|
+
partial output file on any error.
|
|
301
|
+
- **`pipeline/providers.js` `httpPost`/`httpGet` — no request timeout** (#bug): Both helpers
|
|
302
|
+
had no socket timeout, so a hung connection (e.g., LLM provider under load) blocked the
|
|
303
|
+
process indefinitely. A 30-second `timeout` option is now set on every request.
|
|
304
|
+
- **`fact-checker/review-gate.js` `_sendReviewRequest` — dead `escaped` variable** (#cleanup):
|
|
305
|
+
The method built an `escaped` string with MarkdownV2 escaping but then passed the raw
|
|
306
|
+
`message` (not `escaped`) to Telegram with `parse_mode: 'Markdown'`. Raw message with
|
|
307
|
+
unescaped markdown caused Telegram 400 errors. Fixed: removed the unused `escaped` variable
|
|
308
|
+
and dropped `parse_mode` so the message is delivered as plain text.
|
|
309
|
+
- **`fact-checker/review-gate.js` `_waitForDecisions` — silent TIMEOUT_APPROVE** (#bug):
|
|
310
|
+
Timed-out claims were marked `TIMEOUT_APPROVE` and logged only at `warn` level, silently
|
|
311
|
+
releasing the pipeline. They are now marked `TIMEOUT` (neutral decision, visible in state
|
|
312
|
+
machine logs, no auto-approve semantics).
|
|
313
|
+
- **`fact-checker/checker.js` `_searchBrave` — `r.age` NaN year** (#bug): Brave Search returns
|
|
314
|
+
`r.age` as a human-readable string like `"5 days ago"`, which `new Date("5 days ago")` parses
|
|
315
|
+
as `Invalid Date` (year `NaN`). The field `r.page_age` is the ISO date string — used that
|
|
316
|
+
instead, with `new Date().getFullYear()` as the fallback.
|
|
317
|
+
- **`pipeline/artifact-retention.js` `sweepExpiredIntermediates` — shallow scan** (#bug): The
|
|
318
|
+
sweep only descended one directory level from `filesDir`, missing retention tags in
|
|
319
|
+
`video_tmp` and deep production subdirectories. The directory walk is now fully recursive.
|
|
320
|
+
|
|
321
|
+
---
|
|
322
|
+
|
|
323
|
+
## [1.3.1] — 2026-11-02
|
|
324
|
+
|
|
325
|
+
### Fixed
|
|
326
|
+
|
|
327
|
+
- **`server.js` `publish_video` tool — double Telegram delivery** (#bug): The handler sent
|
|
328
|
+
the video inline via `spawnSync('curl')` and then immediately called `runPublishing()`,
|
|
329
|
+
which also sends to Telegram when `channels` includes `'telegram'`. Removed the redundant
|
|
330
|
+
inline curl block; the tool now delegates entirely to `runPublishing()`.
|
|
331
|
+
- **`server.js` `n8n_webhook_trigger` — dead destructure** (#cleanup): `const { https: _https,
|
|
332
|
+
http: _http } = require('https')` was a no-op (the `https` module has no `http` export) and
|
|
333
|
+
the variables were unused. Removed the dead line.
|
|
334
|
+
- **`fact-checker/checker.js` `_searchBrave` — gzip decode corruption** (#bug): The Brave Search
|
|
335
|
+
request included `Accept-Encoding: gzip` but `_httpsGet` collects raw Buffer chunks and decodes
|
|
336
|
+
them as UTF-8 directly. A gzip-compressed response would produce garbled JSON and a parse error.
|
|
337
|
+
Removed the `Accept-Encoding: gzip` header so the server responds with plain JSON.
|
|
338
|
+
- **`renderer.js` `_captionFilter` — dead re-escape** (#cleanup): The caption was already stripped
|
|
339
|
+
of `:` and `%` characters by the preceding regex, making the subsequent `.replace(/:/g, '\\:')` and
|
|
340
|
+
`.replace(/%/g, '\\%')` calls unreachable no-ops. Removed the dead replacements.
|
|
341
|
+
- **`quality-gate/gate.js` — comment/code mismatch** (#docs): The file header said
|
|
342
|
+
`Integrated loudness: -14 LUFS ±1.0 LU` and `True peak: ≤ -1.0 dBTP`, but the actual
|
|
343
|
+
enforcement thresholds in code are ±1.5 LU and -0.5 dBTP. Corrected the comments.
|
|
344
|
+
- **`animation-system.js` `_zoomIn`/`_zoomOut` — frames param unused** (#bug): Both functions
|
|
345
|
+
accepted a `frames` argument but always used a fixed step constant regardless of duration.
|
|
346
|
+
The zoom step is now proportional to `frames` so a 2-second scene zooms at the same visual
|
|
347
|
+
speed as a 10-second scene. A 2× cap prevents snapping on very short scenes.
|
|
348
|
+
- **`retry-policy.js` Script timeout too short** (#bug): The `Script` stage timeout was 3 min,
|
|
349
|
+
but LLM calls with `maxTokens: 32000` on slower providers routinely exceed that, causing
|
|
350
|
+
spurious retries and dead-letters. Raised to 5 min (matching `Research`).
|
|
351
|
+
- **`workflows/assets.js` — null imagePaths in output hash** (#bug): For scenes with a
|
|
352
|
+
user-supplied `video_path`, `imagePath` is `null`. The hash was computed from
|
|
353
|
+
`results.map(r => r.imagePath).join(',')` which included literal `null` strings, making the
|
|
354
|
+
hash content-unstable across runs. Hash is now computed from `sceneId:voicePath` pairs which
|
|
355
|
+
are always populated.
|
|
356
|
+
- **`cli/doctor.js` — incomplete LLM detection** (#bug): The LLM provider check only tested for
|
|
357
|
+
`LLM_BASE_URL` but missed `WEBHOOK_LLM_URL` and `OLLAMA_BASE_URL` / `PROVIDER_LLM=ollama`.
|
|
358
|
+
Ollama users with only `PROVIDER_LLM=ollama` set would receive a false warning. All three
|
|
359
|
+
env-var shapes are now detected.
|
|
360
|
+
|
|
361
|
+
---
|
|
362
|
+
|
|
363
|
+
## [1.2.0] — 2026-11-02
|
|
364
|
+
|
|
365
|
+
### Added
|
|
366
|
+
- **YouTube Data API v3 uploader** — full OAuth2 resumable chunked upload
|
|
367
|
+
(`YOUTUBE_CLIENT_ID` + `YOUTUBE_CLIENT_SECRET` + `YOUTUBE_REFRESH_TOKEN`).
|
|
368
|
+
Uploads default to `private` privacy; set `YOUTUBE_PRIVACY=public|unlisted` to override.
|
|
369
|
+
- **`docs/youtube-upload.md`** — step-by-step OAuth2 setup guide with troubleshooting table.
|
|
370
|
+
- **Real search API support in fact-checker** — set `SEARCH_API_KEY` +
|
|
371
|
+
`SEARCH_PROVIDER=tavily|brave|searxng` to verify claims against live web results.
|
|
372
|
+
Falls back to LLM-synthesized sources when no key is configured.
|
|
373
|
+
- **Asset library download implementation** — `_downloadAsset()` now performs a real
|
|
374
|
+
HTTPS download with redirect-following (up to 5 hops); partial files are cleaned up on error.
|
|
375
|
+
- **Asset library Pollinations integration** — `_callPollinationsAPI()` now downloads the
|
|
376
|
+
generated image to disk instead of returning a URL string.
|
|
377
|
+
- **Asset library FFmpeg composition** — `composeCharacter()` uses `ffmpeg overlay` filter
|
|
378
|
+
to layer identity + pose + emotion PNGs; falls back gracefully to pose-only if FFmpeg fails.
|
|
379
|
+
- **Asset library keyword search** — `_semanticSearch()` implements term-overlap scoring
|
|
380
|
+
against `visual_description`, `tags`, and metadata fields (no embedding API required).
|
|
381
|
+
- **`log-redaction.js` expanded** — added patterns for ElevenLabs (`sk_`), Tavily (`tvly-`),
|
|
382
|
+
Brave Search (`BSA`), fal.ai (`key_`), Replicate (`r8_`), 32-64 char hex internal tokens,
|
|
383
|
+
and new JSON key names for webhook URLs, MCP auth, YouTube, and search credentials.
|
|
384
|
+
- **`CONTRIBUTING.md`** — contributor guide with PR workflow and coding standards.
|
|
385
|
+
- **`SECURITY.md`** — vulnerability disclosure policy.
|
|
386
|
+
- **`LICENSE`** — MIT license.
|
|
387
|
+
- **`CHANGELOG.md`** — this file.
|
|
388
|
+
|
|
389
|
+
### Changed
|
|
390
|
+
- `_logForReview()` in `publishing.js` now uses the already-imported `fsSync` instead of
|
|
391
|
+
re-requiring `fs` at call time.
|
|
392
|
+
- Commit message convention locked: no `feat:`/`fix:`/`docs:` prefixes — plain imperative only.
|
|
393
|
+
- Git author set to `AMG555 <amgt503@gmail.com>`.
|
|
394
|
+
|
|
395
|
+
---
|
|
396
|
+
|
|
397
|
+
## [1.1.0] — 2026-11-02
|
|
398
|
+
|
|
399
|
+
### Added
|
|
400
|
+
- **`src/cli/doctor.js`** — cross-platform pre-flight checker (Node.js, ffmpeg, ffprobe,
|
|
401
|
+
edge-tts, fonts, env vars, Telegram credentials).
|
|
402
|
+
- **`src/cli/make.js`** — standalone one-command video generator (`npm run make -- --topic "..."`).
|
|
403
|
+
Supports `--dry-run`, `--no-shorts`, `--channels`, `--out-dir`, `--style`, `--env`.
|
|
404
|
+
- **`src/mcp-server/stdio.js`** — zero-port stdio MCP transport with 8 tools, lazy loading,
|
|
405
|
+
and in-process async job registry. Works with , Cursor, Windsurf, Kiro.
|
|
406
|
+
- **Webhook provider adapters** — `WebhookLLMProvider`, `WebhookTTSProvider`,
|
|
407
|
+
`WebhookImageProvider` in `src/pipeline/providers.js`. Configure any HTTP API via env
|
|
408
|
+
vars without code changes.
|
|
409
|
+
- **User-supplied media bypass** — `scene.video_path`, `scene.image_path`, `scene.audio_path`
|
|
410
|
+
skip AI generation entirely per scene.
|
|
411
|
+
- **`cloudflare-mcp/README.md`** — complete Cloudflare Workers deploy guide.
|
|
412
|
+
|
|
413
|
+
### Fixed
|
|
414
|
+
- P0 crash in `research.js`: `checker.checkClaim()` replaced with `checker.checkClaims([c]).then(r => r[0])`.
|
|
415
|
+
- Cross-platform font resolver in `renderer.js` (Windows arialbd/calibrib/segoeui, macOS Helvetica, `FFMPEG_FONT` env override).
|
|
416
|
+
- Stale `rate` parameter removed from `tts.synthesize()` call in `assets.js`.
|
|
417
|
+
|
|
418
|
+
---
|
|
419
|
+
|
|
420
|
+
## [1.0.0] — 2026-11-01
|
|
421
|
+
|
|
422
|
+
### Added
|
|
423
|
+
- Full A→F pipeline: Research → Script → Assets → Render → Shorts → Packaging → Publishing.
|
|
424
|
+
- Cloudflare Workers remote MCP proxy (SSE + Streamable HTTP, ~80% token savings).
|
|
425
|
+
- Render-free split worker (`server-render-free.js`) for Render.com + local Pi/Oracle hybrid.
|
|
426
|
+
- State machine, retry policy, telemetry, artifact manifest, artifact retention.
|
|
427
|
+
- Animation system (10 camera presets), AudioMixer (LUFS -14, sidechain ducking), quality gate.
|
|
428
|
+
- Shorts extractor (silence segmentation + focal-point 9:16 crop).
|
|
429
|
+
- Fact checker with risk classification, 3-way review gate, 100-claim gold standard set.
|
|
430
|
+
- Provider abstraction: Groq, OpenAI, watsonx, Ollama, ElevenLabs, Azure TTS, fal.ai,
|
|
431
|
+
Replicate, DALL-E 3, Pollinations.
|