@amg555/frameforge-mcp 2.3.8 → 2.3.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.
package/CHANGELOG.md CHANGED
@@ -4,6 +4,16 @@ All notable changes to FrameForge are documented here.
4
4
 
5
5
  ---
6
6
 
7
+ ## [2.3.9] — 2026-11-09
8
+
9
+ ### Enhanced
10
+ - **Code-Driven Animation Primitives (`src/video-renderer/scene-animator.js`)**:
11
+ - Expanded `wrapCodeInHarnessHtml` runtime environment with standard animation and easing helpers: `clamp`, `lerp`, `easeIn`, `easeOut`, `easeInOut`, and `boilSeed` (12fps hand-drawn wobble).
12
+ - Explicitly registered `html`, `code`, and `animation_code` in `src/schema/scene.schema.json` for first-class schema validation and tool discovery.
13
+ - Verified pure-function-of-time canvas code execution pipeline: scene code → Playwright virtual clock headless capture → FFmpeg BT.709 H.264/AAC MP4 encode.
14
+
15
+ ---
16
+
7
17
  ## [2.3.8] — 2026-11-09
8
18
 
9
19
  ### Fixed & Enhanced
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@amg555/frameforge-mcp",
3
- "version": "2.3.8",
3
+ "version": "2.3.9",
4
4
  "description": "FrameForge — AI video production engine for agentic IDEs. npx @amg555/frameforge-mcp. FFmpeg + TTS run on your machine. 19 MCP tools.",
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -20,7 +20,7 @@ check('batch handling', code.includes('Array.isArray(body)'));
20
20
  check('ping handler', code.includes("method === 'ping'"));
21
21
  check('notifications/init', code.includes("notifications/initialized"));
22
22
  check('body size limit', code.includes('MAX_BODY_BYTES'));
23
- check('SERVER_INFO version 2.3.8', code.includes("version: '2.3.8'"));
23
+ check('SERVER_INFO version 2.3.9', code.includes("version: '2.3.9'"));
24
24
  check('18 tools defined (incl get_job_status)', code.includes('get_job_status'));
25
25
  check('export_to_editor tool defined', code.includes('export_to_editor'));
26
26
  check('edit_video tool defined', code.includes('edit_video'));
@@ -295,7 +295,7 @@ check('cf-worker has checkRateLimit', cfWorker.includes('checkRateLimit'));
295
295
  check('cf-worker hashes token (SHA-256)', cfWorker.includes('SHA-256'));
296
296
  check('cf-worker uses RATE_LIMITS KV', cfWorker.includes('env.RATE_LIMITS'));
297
297
  check('cf-worker returns 429 on limit', cfWorker.includes('429'));
298
- check('cf-worker version 2.3.8', cfWorker.includes("'2.3.8'"));
298
+ check('cf-worker version 2.3.9', cfWorker.includes("'2.3.9'"));
299
299
  check('wrangler.toml has kv_namespaces',fs.readFileSync('./cloudflare-mcp/wrangler.toml','utf8').includes('kv_namespaces'));
300
300
 
301
301
  // ── plugins/skills present ────────────────────────────────────────────────
@@ -1524,7 +1524,7 @@ Features:
1524
1524
 
1525
1525
  const SERVER_INFO = {
1526
1526
  name: 'video-pipeline',
1527
- version: '2.3.8',
1527
+ version: '2.3.9',
1528
1528
  };
1529
1529
 
1530
1530
  const CAPABILITIES = {
@@ -12,6 +12,9 @@
12
12
  "video_path": { "type": "string", "description": "Optional path to user-supplied video clip or B-roll" },
13
13
  "image_path": { "type": "string", "description": "Optional path to user-supplied static image" },
14
14
  "audio_path": { "type": "string", "description": "Optional path to user-supplied pre-recorded voiceover" },
15
+ "html": { "type": "string", "description": "Optional self-contained HTML animation page conforming to window.seek(t)" },
16
+ "code": { "type": "string", "description": "Optional canvas drawing JavaScript code executed for time t in window.seek" },
17
+ "animation_code": { "type": "string", "description": "Alias for code" },
15
18
  "visual_description": { "type": "string", "minLength": 1 },
16
19
  "character": {
17
20
  "type": "object",
@@ -406,6 +406,21 @@ function sfc32(a, b, c, d) {
406
406
  }
407
407
  const hash = (i) => sfc32(i * 19 + 42, i * 29 + 1337, i * 13 + 7, 99)();
408
408
 
409
+ // Animation Primitives & Easing
410
+ function clamp(val, min = 0, max = 1) { return Math.max(min, Math.min(max, val)); }
411
+ function lerp(t, a, b, t0 = 0, t1 = 1) {
412
+ if (t <= t0) return a;
413
+ if (t >= t1) return b;
414
+ return a + (b - a) * ((t - t0) / (t1 - t0));
415
+ }
416
+ function easeIn(t) { return clamp(t, 0, 1) ** 2; }
417
+ function easeOut(t) { const c = clamp(t, 0, 1); return 1 - (1 - c) ** 2; }
418
+ function easeInOut(t) { const c = clamp(t, 0, 1); return c < 0.5 ? 2 * c * c : 1 - (-2 * c + 2) ** 2 / 2; }
419
+ function boilSeed(t, seed = 0) {
420
+ const frame12 = Math.floor(t * 12);
421
+ return hash(seed * 1000 + frame12);
422
+ }
423
+
409
424
  // Spring dynamics
410
425
  function spring(t, delay = 0, tension = 180, friction = 14) {
411
426
  const dt = t - delay;