storyforge 0.11.5 → 0.12.1

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.
@@ -5,7 +5,7 @@ import {
5
5
  probeDuration,
6
6
  renderPlaceholderShot,
7
7
  runCmd
8
- } from "./chunk-SJ6APEPZ.js";
8
+ } from "./chunk-EA4U7ASE.js";
9
9
  import {
10
10
  log
11
11
  } from "./chunk-GJQ45C5W.js";
@@ -7014,39 +7014,62 @@ var renderManimRunPod = async (shot, ctx) => {
7014
7014
  return localAdapter(shot, ctx);
7015
7015
  }
7016
7016
  ctx.progress.emit({ phase: "preparing", shotId: shot.id });
7017
- const status = await runJob(
7018
- endpointId,
7019
- {
7020
- // Flash unpacks `input` via **kwargs into the handler. Our handler
7021
- // signature is `async def render_manim_scene(input_data: dict)`, so
7022
- // the payload must be nested under `input_data`.
7023
- input: {
7024
- input_data: {
7025
- python_source: shot.manimCode,
7026
- scene_class: "GeneratedScene",
7027
- fps: ctx.fps,
7028
- quality: "high"
7029
- // manim --quality h => 1080p60; we'll downsample at concat
7030
- }
7017
+ const localFallback = ctx.resolvedEngine === "manim+remotion" ? renderManimRemotion : ctx.resolvedEngine === "gemini+manim+remotion" ? renderGeminiManimRemotion : renderManim;
7018
+ let status;
7019
+ try {
7020
+ status = await runJob(
7021
+ endpointId,
7022
+ {
7023
+ // Flash unpacks `input` via **kwargs into the handler. Our handler
7024
+ // signature is `async def render_manim_scene(input_data: dict)`, so
7025
+ // the payload must be nested under `input_data`.
7026
+ input: {
7027
+ input_data: {
7028
+ python_source: shot.manimCode,
7029
+ scene_class: "GeneratedScene",
7030
+ fps: ctx.fps,
7031
+ quality: "high"
7032
+ // manim --quality h => 1080p60; we'll downsample at concat
7033
+ }
7034
+ },
7035
+ executionTimeout: 600
7031
7036
  },
7032
- executionTimeout: 600
7033
- },
7034
- {
7035
- timeoutMs: 12 * 6e4,
7036
- pollIntervalMs: 3e3,
7037
- signal: ctx.signal,
7038
- onPoll: (s) => {
7039
- const phase = s.status === "IN_QUEUE" ? "preparing" : "rendering";
7040
- ctx.progress.emit({
7041
- phase,
7042
- shotId: shot.id,
7043
- warnings: s.delayTime ? [`runpod queue: ${(s.delayTime / 1e3).toFixed(1)}s`] : void 0
7044
- });
7037
+ {
7038
+ timeoutMs: 12 * 6e4,
7039
+ pollIntervalMs: 3e3,
7040
+ signal: ctx.signal,
7041
+ onPoll: (s) => {
7042
+ const phase = s.status === "IN_QUEUE" ? "preparing" : "rendering";
7043
+ ctx.progress.emit({
7044
+ phase,
7045
+ shotId: shot.id,
7046
+ warnings: s.delayTime ? [`runpod queue: ${(s.delayTime / 1e3).toFixed(1)}s`] : void 0
7047
+ });
7048
+ }
7045
7049
  }
7046
- }
7047
- );
7050
+ );
7051
+ } catch (err) {
7052
+ const reason = err.message.slice(0, 200);
7053
+ ctx.progress.emit({
7054
+ phase: "preparing",
7055
+ shotId: shot.id,
7056
+ warnings: [`runpod-manim runtime failure \u2192 falling back to local manim: ${reason}`]
7057
+ });
7058
+ return localFallback(shot, ctx);
7059
+ }
7048
7060
  ctx.progress.emit({ phase: "compositing", shotId: shot.id, percent: 90 });
7049
- const buf = decodeBase64Mp4(status.output);
7061
+ let buf;
7062
+ try {
7063
+ buf = decodeBase64Mp4(status.output);
7064
+ } catch (err) {
7065
+ const reason = err.message.slice(0, 200);
7066
+ ctx.progress.emit({
7067
+ phase: "preparing",
7068
+ shotId: shot.id,
7069
+ warnings: [`runpod-manim decode failure \u2192 falling back to local manim: ${reason}`]
7070
+ });
7071
+ return localFallback(shot, ctx);
7072
+ }
7050
7073
  fs13.writeFileSync(ctx.sceneOutPath, buf);
7051
7074
  if (status.executionTime) {
7052
7075
  const cost = status.executionTime / 1e3 * 19e-5;
@@ -8117,7 +8140,7 @@ var BridgePoller = class {
8117
8140
  async invokeStitchFinal(jobId, payload) {
8118
8141
  let stitchFinalVideo;
8119
8142
  try {
8120
- const mod = await import("./stitch-MRZJ34KA.js");
8143
+ const mod = await import("./stitch-Y35UHIBV.js");
8121
8144
  stitchFinalVideo = mod.stitchFinalVideo;
8122
8145
  } catch (err) {
8123
8146
  return {
@@ -90,8 +90,26 @@ function buildConcatArgs(manifestPath, outPath) {
90
90
  "0",
91
91
  "-i",
92
92
  manifestPath,
93
- "-c",
94
- "copy",
93
+ "-c:v",
94
+ "libx264",
95
+ "-preset",
96
+ "medium",
97
+ "-crf",
98
+ "18",
99
+ "-pix_fmt",
100
+ "yuv420p",
101
+ "-r",
102
+ "30",
103
+ "-vsync",
104
+ "cfr",
105
+ // Audio is optional in scene files; -c:a aac is safe even when
106
+ // some scenes are silent (lavfi placeholders) — ffmpeg synthesizes
107
+ // a silent audio track to match. If the scenes are all silent,
108
+ // the output also ends up silent and audio gets added by muxAudio.
109
+ "-c:a",
110
+ "aac",
111
+ "-b:a",
112
+ "192k",
95
113
  outPath
96
114
  ];
97
115
  }
package/dist/index.js CHANGED
@@ -84,6 +84,52 @@ function loadDotEnv() {
84
84
  }
85
85
  return { loaded, sources };
86
86
  }
87
+ async function fetchBridgeCredentials(opts) {
88
+ const fetchImpl = opts.fetchImpl ?? fetch;
89
+ const timeoutMs = opts.timeoutMs ?? 1e4;
90
+ const url = `${opts.apiUrl.replace(/\/$/, "")}/api/cli-bridge/credentials`;
91
+ const ctrl = new AbortController();
92
+ const timer = setTimeout(() => ctrl.abort(), timeoutMs);
93
+ let resp;
94
+ try {
95
+ resp = await fetchImpl(url, {
96
+ method: "GET",
97
+ headers: {
98
+ Authorization: `Bearer ${opts.token}`,
99
+ Accept: "application/json"
100
+ },
101
+ signal: ctrl.signal
102
+ });
103
+ } catch (err) {
104
+ return { loaded: [], source: url, error: err.message };
105
+ } finally {
106
+ clearTimeout(timer);
107
+ }
108
+ if (!resp.ok) {
109
+ const text = await resp.text().catch(() => "");
110
+ return { loaded: [], source: url, error: `HTTP ${resp.status}: ${text.slice(0, 200)}` };
111
+ }
112
+ let body;
113
+ try {
114
+ body = await resp.json();
115
+ } catch (err) {
116
+ return { loaded: [], source: url, error: `parse: ${err.message}` };
117
+ }
118
+ const creds = body.credentials ?? {};
119
+ const loaded = [];
120
+ for (const [key, val] of Object.entries(creds)) {
121
+ if (typeof val !== "string" || val.length === 0) continue;
122
+ if (process.env[key] != null) continue;
123
+ process.env[key] = val;
124
+ loaded.push(key);
125
+ }
126
+ return {
127
+ loaded,
128
+ source: url,
129
+ knownCount: body.knownCount,
130
+ availableCount: body.availableCount
131
+ };
132
+ }
87
133
 
88
134
  // src/utils/script-prompt.ts
89
135
  var STYLE_GUIDES = {
@@ -855,6 +901,18 @@ async function devCommand(options) {
855
901
  if (env.loaded.length > 0) {
856
902
  log.info(`Loaded env: ${env.loaded.join(", ")} from ${env.sources.join(", ")}`);
857
903
  }
904
+ const bridgeToken = process.env.FORGE_BRIDGE_TOKEN ?? process.env.BRIDGE_TOKEN ?? "";
905
+ if (bridgeToken) {
906
+ const apiUrl = process.env.FORGE_BRIDGE_URL ?? WEB_URL;
907
+ const credResult = await fetchBridgeCredentials({ apiUrl, token: bridgeToken });
908
+ if (credResult.error) {
909
+ log.info(`Bridge cred fetch skipped: ${credResult.error}`);
910
+ } else if (credResult.loaded.length > 0) {
911
+ log.info(`Loaded ${credResult.loaded.length} cloud creds from Vercel: ${credResult.loaded.join(", ")}`);
912
+ } else if (credResult.availableCount != null) {
913
+ log.info(`Bridge cred fetch: 0 new keys (server has ${credResult.availableCount}/${credResult.knownCount ?? "?"}, all already set locally)`);
914
+ }
915
+ }
858
916
  void (async () => {
859
917
  try {
860
918
  const { gatherCliUsage, CLI_USAGE_PARSER_VERSION } = await import("./cli-usage-G762TREV.js");
@@ -1619,8 +1677,8 @@ Return ONLY the complete updated TSX. No markdown fences, no explanation.`;
1619
1677
  console.log(` POST http://localhost:${port}/api/script-gen`);
1620
1678
  }
1621
1679
  const bridgeUrl = process.env.FORGE_BRIDGE_URL ?? WEB_URL;
1622
- const bridgeToken = process.env.FORGE_BRIDGE_TOKEN ?? process.env.BRIDGE_TOKEN ?? "";
1623
- if (bridgeToken) {
1680
+ const bridgeToken2 = process.env.FORGE_BRIDGE_TOKEN ?? process.env.BRIDGE_TOKEN ?? "";
1681
+ if (bridgeToken2) {
1624
1682
  const pkgVersion = (() => {
1625
1683
  try {
1626
1684
  const here = path2.dirname(new URL(import.meta.url).pathname);
@@ -1631,8 +1689,8 @@ Return ONLY the complete updated TSX. No markdown fences, no explanation.`;
1631
1689
  return "0.0.0";
1632
1690
  })();
1633
1691
  void (async () => {
1634
- const { BridgePoller } = await import("./bridge-poller-6AWMIF3V.js");
1635
- const poller = new BridgePoller({ baseUrl: bridgeUrl, token: bridgeToken, clientVersion: `storyforge ${pkgVersion}` });
1692
+ const { BridgePoller } = await import("./bridge-poller-APNLBMPW.js");
1693
+ const poller = new BridgePoller({ baseUrl: bridgeUrl, token: bridgeToken2, clientVersion: `storyforge ${pkgVersion}` });
1636
1694
  poller.start();
1637
1695
  })();
1638
1696
  console.log("");
@@ -2454,7 +2512,7 @@ function resolveBridgeToken2(explicit) {
2454
2512
  // package.json
2455
2513
  var package_default = {
2456
2514
  name: "storyforge",
2457
- version: "0.11.5",
2515
+ version: "0.12.1",
2458
2516
  description: "StoryForge \u2014 local bridge for the Forge video production web app. Parallel clip-render orchestrator (Remotion 4 + Manim + HyperFrames + ffmpeg) + final video stitcher + dependency doctor.",
2459
2517
  type: "module",
2460
2518
  bin: {
@@ -5,7 +5,7 @@ import {
5
5
  buildNormalizeClipArgs,
6
6
  probeDuration,
7
7
  runCmd
8
- } from "./chunk-SJ6APEPZ.js";
8
+ } from "./chunk-EA4U7ASE.js";
9
9
  import "./chunk-NSPRIPOP.js";
10
10
 
11
11
  // ../pipeline/src/stitch/stitch-final.ts
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "storyforge",
3
- "version": "0.11.5",
3
+ "version": "0.12.1",
4
4
  "description": "StoryForge — local bridge for the Forge video production web app. Parallel clip-render orchestrator (Remotion 4 + Manim + HyperFrames + ffmpeg) + final video stitcher + dependency doctor.",
5
5
  "type": "module",
6
6
  "bin": {