@vibeframe/cli 0.55.0 → 0.55.2

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.
Files changed (497) hide show
  1. package/dist/index.js +63450 -233
  2. package/package.json +11 -6
  3. package/dist/agent/adapters/claude.d.ts +0 -15
  4. package/dist/agent/adapters/claude.d.ts.map +0 -1
  5. package/dist/agent/adapters/claude.js +0 -119
  6. package/dist/agent/adapters/claude.js.map +0 -1
  7. package/dist/agent/adapters/gemini.d.ts +0 -15
  8. package/dist/agent/adapters/gemini.d.ts.map +0 -1
  9. package/dist/agent/adapters/gemini.js +0 -132
  10. package/dist/agent/adapters/gemini.js.map +0 -1
  11. package/dist/agent/adapters/index.d.ts +0 -28
  12. package/dist/agent/adapters/index.d.ts.map +0 -1
  13. package/dist/agent/adapters/index.js +0 -43
  14. package/dist/agent/adapters/index.js.map +0 -1
  15. package/dist/agent/adapters/ollama.d.ts +0 -20
  16. package/dist/agent/adapters/ollama.d.ts.map +0 -1
  17. package/dist/agent/adapters/ollama.js +0 -186
  18. package/dist/agent/adapters/ollama.js.map +0 -1
  19. package/dist/agent/adapters/openai.d.ts +0 -15
  20. package/dist/agent/adapters/openai.d.ts.map +0 -1
  21. package/dist/agent/adapters/openai.js +0 -92
  22. package/dist/agent/adapters/openai.js.map +0 -1
  23. package/dist/agent/adapters/openrouter.d.ts +0 -16
  24. package/dist/agent/adapters/openrouter.d.ts.map +0 -1
  25. package/dist/agent/adapters/openrouter.js +0 -100
  26. package/dist/agent/adapters/openrouter.js.map +0 -1
  27. package/dist/agent/adapters/openrouter.test.d.ts +0 -5
  28. package/dist/agent/adapters/openrouter.test.d.ts.map +0 -1
  29. package/dist/agent/adapters/openrouter.test.js +0 -73
  30. package/dist/agent/adapters/openrouter.test.js.map +0 -1
  31. package/dist/agent/adapters/xai.d.ts +0 -15
  32. package/dist/agent/adapters/xai.d.ts.map +0 -1
  33. package/dist/agent/adapters/xai.js +0 -95
  34. package/dist/agent/adapters/xai.js.map +0 -1
  35. package/dist/agent/index.d.ts +0 -69
  36. package/dist/agent/index.d.ts.map +0 -1
  37. package/dist/agent/index.js +0 -180
  38. package/dist/agent/index.js.map +0 -1
  39. package/dist/agent/memory/index.d.ts +0 -70
  40. package/dist/agent/memory/index.d.ts.map +0 -1
  41. package/dist/agent/memory/index.js +0 -132
  42. package/dist/agent/memory/index.js.map +0 -1
  43. package/dist/agent/prompts/system.d.ts +0 -6
  44. package/dist/agent/prompts/system.d.ts.map +0 -1
  45. package/dist/agent/prompts/system.js +0 -103
  46. package/dist/agent/prompts/system.js.map +0 -1
  47. package/dist/agent/tools/ai-editing.d.ts +0 -15
  48. package/dist/agent/tools/ai-editing.d.ts.map +0 -1
  49. package/dist/agent/tools/ai-editing.js +0 -763
  50. package/dist/agent/tools/ai-editing.js.map +0 -1
  51. package/dist/agent/tools/ai-generation.d.ts +0 -13
  52. package/dist/agent/tools/ai-generation.d.ts.map +0 -1
  53. package/dist/agent/tools/ai-generation.js +0 -973
  54. package/dist/agent/tools/ai-generation.js.map +0 -1
  55. package/dist/agent/tools/ai-pipeline.d.ts +0 -14
  56. package/dist/agent/tools/ai-pipeline.d.ts.map +0 -1
  57. package/dist/agent/tools/ai-pipeline.js +0 -961
  58. package/dist/agent/tools/ai-pipeline.js.map +0 -1
  59. package/dist/agent/tools/ai.d.ts +0 -13
  60. package/dist/agent/tools/ai.d.ts.map +0 -1
  61. package/dist/agent/tools/ai.js +0 -19
  62. package/dist/agent/tools/ai.js.map +0 -1
  63. package/dist/agent/tools/batch.d.ts +0 -6
  64. package/dist/agent/tools/batch.d.ts.map +0 -1
  65. package/dist/agent/tools/batch.js +0 -383
  66. package/dist/agent/tools/batch.js.map +0 -1
  67. package/dist/agent/tools/e2e.test.d.ts +0 -26
  68. package/dist/agent/tools/e2e.test.d.ts.map +0 -1
  69. package/dist/agent/tools/e2e.test.js +0 -397
  70. package/dist/agent/tools/e2e.test.js.map +0 -1
  71. package/dist/agent/tools/export.d.ts +0 -6
  72. package/dist/agent/tools/export.d.ts.map +0 -1
  73. package/dist/agent/tools/export.js +0 -171
  74. package/dist/agent/tools/export.js.map +0 -1
  75. package/dist/agent/tools/filesystem.d.ts +0 -6
  76. package/dist/agent/tools/filesystem.d.ts.map +0 -1
  77. package/dist/agent/tools/filesystem.js +0 -212
  78. package/dist/agent/tools/filesystem.js.map +0 -1
  79. package/dist/agent/tools/index.d.ts +0 -66
  80. package/dist/agent/tools/index.d.ts.map +0 -1
  81. package/dist/agent/tools/index.js +0 -123
  82. package/dist/agent/tools/index.js.map +0 -1
  83. package/dist/agent/tools/integration.test.d.ts +0 -11
  84. package/dist/agent/tools/integration.test.d.ts.map +0 -1
  85. package/dist/agent/tools/integration.test.js +0 -659
  86. package/dist/agent/tools/integration.test.js.map +0 -1
  87. package/dist/agent/tools/media.d.ts +0 -6
  88. package/dist/agent/tools/media.d.ts.map +0 -1
  89. package/dist/agent/tools/media.js +0 -616
  90. package/dist/agent/tools/media.js.map +0 -1
  91. package/dist/agent/tools/project.d.ts +0 -6
  92. package/dist/agent/tools/project.d.ts.map +0 -1
  93. package/dist/agent/tools/project.js +0 -284
  94. package/dist/agent/tools/project.js.map +0 -1
  95. package/dist/agent/tools/scene.d.ts +0 -17
  96. package/dist/agent/tools/scene.d.ts.map +0 -1
  97. package/dist/agent/tools/scene.js +0 -263
  98. package/dist/agent/tools/scene.js.map +0 -1
  99. package/dist/agent/tools/scene.test.d.ts +0 -2
  100. package/dist/agent/tools/scene.test.d.ts.map +0 -1
  101. package/dist/agent/tools/scene.test.js +0 -139
  102. package/dist/agent/tools/scene.test.js.map +0 -1
  103. package/dist/agent/tools/timeline.d.ts +0 -6
  104. package/dist/agent/tools/timeline.d.ts.map +0 -1
  105. package/dist/agent/tools/timeline.js +0 -875
  106. package/dist/agent/tools/timeline.js.map +0 -1
  107. package/dist/agent/types.d.ts +0 -59
  108. package/dist/agent/types.d.ts.map +0 -1
  109. package/dist/agent/types.js +0 -5
  110. package/dist/agent/types.js.map +0 -1
  111. package/dist/commands/_shared/scene-audio-mux.d.ts +0 -75
  112. package/dist/commands/_shared/scene-audio-mux.d.ts.map +0 -1
  113. package/dist/commands/_shared/scene-audio-mux.js +0 -159
  114. package/dist/commands/_shared/scene-audio-mux.js.map +0 -1
  115. package/dist/commands/_shared/scene-audio-mux.test.d.ts +0 -7
  116. package/dist/commands/_shared/scene-audio-mux.test.d.ts.map +0 -1
  117. package/dist/commands/_shared/scene-audio-mux.test.js +0 -94
  118. package/dist/commands/_shared/scene-audio-mux.test.js.map +0 -1
  119. package/dist/commands/_shared/scene-audio-scan.d.ts +0 -94
  120. package/dist/commands/_shared/scene-audio-scan.d.ts.map +0 -1
  121. package/dist/commands/_shared/scene-audio-scan.js +0 -144
  122. package/dist/commands/_shared/scene-audio-scan.js.map +0 -1
  123. package/dist/commands/_shared/scene-audio-scan.test.d.ts +0 -6
  124. package/dist/commands/_shared/scene-audio-scan.test.d.ts.map +0 -1
  125. package/dist/commands/_shared/scene-audio-scan.test.js +0 -190
  126. package/dist/commands/_shared/scene-audio-scan.test.js.map +0 -1
  127. package/dist/commands/_shared/scene-html-emit.d.ts +0 -120
  128. package/dist/commands/_shared/scene-html-emit.d.ts.map +0 -1
  129. package/dist/commands/_shared/scene-html-emit.js +0 -422
  130. package/dist/commands/_shared/scene-html-emit.js.map +0 -1
  131. package/dist/commands/_shared/scene-html-emit.test.d.ts +0 -2
  132. package/dist/commands/_shared/scene-html-emit.test.d.ts.map +0 -1
  133. package/dist/commands/_shared/scene-html-emit.test.js +0 -396
  134. package/dist/commands/_shared/scene-html-emit.test.js.map +0 -1
  135. package/dist/commands/_shared/scene-lint.d.ts +0 -110
  136. package/dist/commands/_shared/scene-lint.d.ts.map +0 -1
  137. package/dist/commands/_shared/scene-lint.js +0 -207
  138. package/dist/commands/_shared/scene-lint.js.map +0 -1
  139. package/dist/commands/_shared/scene-lint.test.d.ts +0 -2
  140. package/dist/commands/_shared/scene-lint.test.d.ts.map +0 -1
  141. package/dist/commands/_shared/scene-lint.test.js +0 -208
  142. package/dist/commands/_shared/scene-lint.test.js.map +0 -1
  143. package/dist/commands/_shared/scene-project.d.ts +0 -101
  144. package/dist/commands/_shared/scene-project.d.ts.map +0 -1
  145. package/dist/commands/_shared/scene-project.js +0 -281
  146. package/dist/commands/_shared/scene-project.js.map +0 -1
  147. package/dist/commands/_shared/scene-project.test.d.ts +0 -2
  148. package/dist/commands/_shared/scene-project.test.d.ts.map +0 -1
  149. package/dist/commands/_shared/scene-project.test.js +0 -203
  150. package/dist/commands/_shared/scene-project.test.js.map +0 -1
  151. package/dist/commands/_shared/scene-render.d.ts +0 -85
  152. package/dist/commands/_shared/scene-render.d.ts.map +0 -1
  153. package/dist/commands/_shared/scene-render.js +0 -178
  154. package/dist/commands/_shared/scene-render.js.map +0 -1
  155. package/dist/commands/_shared/scene-render.test.d.ts +0 -2
  156. package/dist/commands/_shared/scene-render.test.d.ts.map +0 -1
  157. package/dist/commands/_shared/scene-render.test.js +0 -145
  158. package/dist/commands/_shared/scene-render.test.js.map +0 -1
  159. package/dist/commands/_shared/segments-to-scenes.d.ts +0 -85
  160. package/dist/commands/_shared/segments-to-scenes.d.ts.map +0 -1
  161. package/dist/commands/_shared/segments-to-scenes.js +0 -202
  162. package/dist/commands/_shared/segments-to-scenes.js.map +0 -1
  163. package/dist/commands/_shared/segments-to-scenes.test.d.ts +0 -2
  164. package/dist/commands/_shared/segments-to-scenes.test.d.ts.map +0 -1
  165. package/dist/commands/_shared/segments-to-scenes.test.js +0 -219
  166. package/dist/commands/_shared/segments-to-scenes.test.js.map +0 -1
  167. package/dist/commands/_shared/tts-resolve.d.ts +0 -75
  168. package/dist/commands/_shared/tts-resolve.d.ts.map +0 -1
  169. package/dist/commands/_shared/tts-resolve.js +0 -89
  170. package/dist/commands/_shared/tts-resolve.js.map +0 -1
  171. package/dist/commands/_shared/tts-resolve.test.d.ts +0 -6
  172. package/dist/commands/_shared/tts-resolve.test.d.ts.map +0 -1
  173. package/dist/commands/_shared/tts-resolve.test.js +0 -147
  174. package/dist/commands/_shared/tts-resolve.test.js.map +0 -1
  175. package/dist/commands/agent.d.ts +0 -21
  176. package/dist/commands/agent.d.ts.map +0 -1
  177. package/dist/commands/agent.js +0 -285
  178. package/dist/commands/agent.js.map +0 -1
  179. package/dist/commands/ai-analyze.d.ts +0 -106
  180. package/dist/commands/ai-analyze.d.ts.map +0 -1
  181. package/dist/commands/ai-analyze.js +0 -320
  182. package/dist/commands/ai-analyze.js.map +0 -1
  183. package/dist/commands/ai-animated-caption.d.ts +0 -64
  184. package/dist/commands/ai-animated-caption.d.ts.map +0 -1
  185. package/dist/commands/ai-animated-caption.js +0 -272
  186. package/dist/commands/ai-animated-caption.js.map +0 -1
  187. package/dist/commands/ai-audio.d.ts +0 -98
  188. package/dist/commands/ai-audio.d.ts.map +0 -1
  189. package/dist/commands/ai-audio.js +0 -1059
  190. package/dist/commands/ai-audio.js.map +0 -1
  191. package/dist/commands/ai-broll.d.ts +0 -15
  192. package/dist/commands/ai-broll.d.ts.map +0 -1
  193. package/dist/commands/ai-broll.js +0 -421
  194. package/dist/commands/ai-broll.js.map +0 -1
  195. package/dist/commands/ai-edit-cli.d.ts +0 -14
  196. package/dist/commands/ai-edit-cli.d.ts.map +0 -1
  197. package/dist/commands/ai-edit-cli.js +0 -585
  198. package/dist/commands/ai-edit-cli.js.map +0 -1
  199. package/dist/commands/ai-edit.d.ts +0 -398
  200. package/dist/commands/ai-edit.d.ts.map +0 -1
  201. package/dist/commands/ai-edit.js +0 -1060
  202. package/dist/commands/ai-edit.js.map +0 -1
  203. package/dist/commands/ai-fill-gaps.d.ts +0 -14
  204. package/dist/commands/ai-fill-gaps.d.ts.map +0 -1
  205. package/dist/commands/ai-fill-gaps.js +0 -447
  206. package/dist/commands/ai-fill-gaps.js.map +0 -1
  207. package/dist/commands/ai-helpers.d.ts +0 -20
  208. package/dist/commands/ai-helpers.d.ts.map +0 -1
  209. package/dist/commands/ai-helpers.js +0 -59
  210. package/dist/commands/ai-helpers.js.map +0 -1
  211. package/dist/commands/ai-highlights.d.ts +0 -127
  212. package/dist/commands/ai-highlights.d.ts.map +0 -1
  213. package/dist/commands/ai-highlights.js +0 -1053
  214. package/dist/commands/ai-highlights.js.map +0 -1
  215. package/dist/commands/ai-image.d.ts +0 -76
  216. package/dist/commands/ai-image.d.ts.map +0 -1
  217. package/dist/commands/ai-image.js +0 -858
  218. package/dist/commands/ai-image.js.map +0 -1
  219. package/dist/commands/ai-motion.d.ts +0 -50
  220. package/dist/commands/ai-motion.d.ts.map +0 -1
  221. package/dist/commands/ai-motion.js +0 -297
  222. package/dist/commands/ai-motion.js.map +0 -1
  223. package/dist/commands/ai-narrate.d.ts +0 -66
  224. package/dist/commands/ai-narrate.d.ts.map +0 -1
  225. package/dist/commands/ai-narrate.js +0 -332
  226. package/dist/commands/ai-narrate.js.map +0 -1
  227. package/dist/commands/ai-review.d.ts +0 -57
  228. package/dist/commands/ai-review.d.ts.map +0 -1
  229. package/dist/commands/ai-review.js +0 -270
  230. package/dist/commands/ai-review.js.map +0 -1
  231. package/dist/commands/ai-script-pipeline-cli.d.ts +0 -9
  232. package/dist/commands/ai-script-pipeline-cli.d.ts.map +0 -1
  233. package/dist/commands/ai-script-pipeline-cli.js +0 -502
  234. package/dist/commands/ai-script-pipeline-cli.js.map +0 -1
  235. package/dist/commands/ai-script-pipeline.d.ts +0 -305
  236. package/dist/commands/ai-script-pipeline.d.ts.map +0 -1
  237. package/dist/commands/ai-script-pipeline.js +0 -1546
  238. package/dist/commands/ai-script-pipeline.js.map +0 -1
  239. package/dist/commands/ai-suggest-edit.d.ts +0 -14
  240. package/dist/commands/ai-suggest-edit.d.ts.map +0 -1
  241. package/dist/commands/ai-suggest-edit.js +0 -215
  242. package/dist/commands/ai-suggest-edit.js.map +0 -1
  243. package/dist/commands/ai-video-fx.d.ts +0 -14
  244. package/dist/commands/ai-video-fx.d.ts.map +0 -1
  245. package/dist/commands/ai-video-fx.js +0 -396
  246. package/dist/commands/ai-video-fx.js.map +0 -1
  247. package/dist/commands/ai-video.d.ts +0 -91
  248. package/dist/commands/ai-video.d.ts.map +0 -1
  249. package/dist/commands/ai-video.js +0 -1055
  250. package/dist/commands/ai-video.js.map +0 -1
  251. package/dist/commands/ai-viral.d.ts +0 -15
  252. package/dist/commands/ai-viral.d.ts.map +0 -1
  253. package/dist/commands/ai-viral.js +0 -507
  254. package/dist/commands/ai-viral.js.map +0 -1
  255. package/dist/commands/ai-visual-fx.d.ts +0 -14
  256. package/dist/commands/ai-visual-fx.d.ts.map +0 -1
  257. package/dist/commands/ai-visual-fx.js +0 -521
  258. package/dist/commands/ai-visual-fx.js.map +0 -1
  259. package/dist/commands/ai.d.ts +0 -38
  260. package/dist/commands/ai.d.ts.map +0 -1
  261. package/dist/commands/ai.js +0 -225
  262. package/dist/commands/ai.js.map +0 -1
  263. package/dist/commands/ai.test.d.ts +0 -2
  264. package/dist/commands/ai.test.d.ts.map +0 -1
  265. package/dist/commands/ai.test.js +0 -557
  266. package/dist/commands/ai.test.js.map +0 -1
  267. package/dist/commands/analyze.d.ts +0 -16
  268. package/dist/commands/analyze.d.ts.map +0 -1
  269. package/dist/commands/analyze.js +0 -247
  270. package/dist/commands/analyze.js.map +0 -1
  271. package/dist/commands/audio.d.ts +0 -18
  272. package/dist/commands/audio.d.ts.map +0 -1
  273. package/dist/commands/audio.js +0 -539
  274. package/dist/commands/audio.js.map +0 -1
  275. package/dist/commands/batch.d.ts +0 -3
  276. package/dist/commands/batch.d.ts.map +0 -1
  277. package/dist/commands/batch.js +0 -419
  278. package/dist/commands/batch.js.map +0 -1
  279. package/dist/commands/batch.test.d.ts +0 -2
  280. package/dist/commands/batch.test.d.ts.map +0 -1
  281. package/dist/commands/batch.test.js +0 -203
  282. package/dist/commands/batch.test.js.map +0 -1
  283. package/dist/commands/context.d.ts +0 -7
  284. package/dist/commands/context.d.ts.map +0 -1
  285. package/dist/commands/context.js +0 -66
  286. package/dist/commands/context.js.map +0 -1
  287. package/dist/commands/demo.d.ts +0 -7
  288. package/dist/commands/demo.d.ts.map +0 -1
  289. package/dist/commands/demo.js +0 -192
  290. package/dist/commands/demo.js.map +0 -1
  291. package/dist/commands/detect.d.ts +0 -56
  292. package/dist/commands/detect.d.ts.map +0 -1
  293. package/dist/commands/detect.js +0 -464
  294. package/dist/commands/detect.js.map +0 -1
  295. package/dist/commands/doctor.d.ts +0 -6
  296. package/dist/commands/doctor.d.ts.map +0 -1
  297. package/dist/commands/doctor.js +0 -322
  298. package/dist/commands/doctor.js.map +0 -1
  299. package/dist/commands/edit-cmd.d.ts +0 -112
  300. package/dist/commands/edit-cmd.d.ts.map +0 -1
  301. package/dist/commands/edit-cmd.js +0 -1041
  302. package/dist/commands/edit-cmd.js.map +0 -1
  303. package/dist/commands/export.d.ts +0 -61
  304. package/dist/commands/export.d.ts.map +0 -1
  305. package/dist/commands/export.js +0 -980
  306. package/dist/commands/export.js.map +0 -1
  307. package/dist/commands/export.test.d.ts +0 -2
  308. package/dist/commands/export.test.d.ts.map +0 -1
  309. package/dist/commands/export.test.js +0 -107
  310. package/dist/commands/export.test.js.map +0 -1
  311. package/dist/commands/generate.d.ts +0 -84
  312. package/dist/commands/generate.d.ts.map +0 -1
  313. package/dist/commands/generate.js +0 -2030
  314. package/dist/commands/generate.js.map +0 -1
  315. package/dist/commands/media.d.ts +0 -3
  316. package/dist/commands/media.d.ts.map +0 -1
  317. package/dist/commands/media.js +0 -163
  318. package/dist/commands/media.js.map +0 -1
  319. package/dist/commands/output.d.ts +0 -51
  320. package/dist/commands/output.d.ts.map +0 -1
  321. package/dist/commands/output.js +0 -219
  322. package/dist/commands/output.js.map +0 -1
  323. package/dist/commands/output.test.d.ts +0 -2
  324. package/dist/commands/output.test.d.ts.map +0 -1
  325. package/dist/commands/output.test.js +0 -151
  326. package/dist/commands/output.test.js.map +0 -1
  327. package/dist/commands/pipeline.d.ts +0 -19
  328. package/dist/commands/pipeline.d.ts.map +0 -1
  329. package/dist/commands/pipeline.js +0 -345
  330. package/dist/commands/pipeline.js.map +0 -1
  331. package/dist/commands/project.d.ts +0 -3
  332. package/dist/commands/project.d.ts.map +0 -1
  333. package/dist/commands/project.js +0 -181
  334. package/dist/commands/project.js.map +0 -1
  335. package/dist/commands/project.test.d.ts +0 -2
  336. package/dist/commands/project.test.d.ts.map +0 -1
  337. package/dist/commands/project.test.js +0 -105
  338. package/dist/commands/project.test.js.map +0 -1
  339. package/dist/commands/run.d.ts +0 -15
  340. package/dist/commands/run.d.ts.map +0 -1
  341. package/dist/commands/run.js +0 -204
  342. package/dist/commands/run.js.map +0 -1
  343. package/dist/commands/sanitize.d.ts +0 -21
  344. package/dist/commands/sanitize.d.ts.map +0 -1
  345. package/dist/commands/sanitize.js +0 -56
  346. package/dist/commands/sanitize.js.map +0 -1
  347. package/dist/commands/scene.d.ts +0 -86
  348. package/dist/commands/scene.d.ts.map +0 -1
  349. package/dist/commands/scene.js +0 -728
  350. package/dist/commands/scene.js.map +0 -1
  351. package/dist/commands/scene.test.d.ts +0 -2
  352. package/dist/commands/scene.test.d.ts.map +0 -1
  353. package/dist/commands/scene.test.js +0 -219
  354. package/dist/commands/scene.test.js.map +0 -1
  355. package/dist/commands/schema.d.ts +0 -13
  356. package/dist/commands/schema.d.ts.map +0 -1
  357. package/dist/commands/schema.js +0 -218
  358. package/dist/commands/schema.js.map +0 -1
  359. package/dist/commands/setup.d.ts +0 -6
  360. package/dist/commands/setup.d.ts.map +0 -1
  361. package/dist/commands/setup.js +0 -461
  362. package/dist/commands/setup.js.map +0 -1
  363. package/dist/commands/timeline.d.ts +0 -3
  364. package/dist/commands/timeline.d.ts.map +0 -1
  365. package/dist/commands/timeline.js +0 -609
  366. package/dist/commands/timeline.js.map +0 -1
  367. package/dist/commands/timeline.test.d.ts +0 -2
  368. package/dist/commands/timeline.test.d.ts.map +0 -1
  369. package/dist/commands/timeline.test.js +0 -334
  370. package/dist/commands/timeline.test.js.map +0 -1
  371. package/dist/commands/validate.d.ts +0 -34
  372. package/dist/commands/validate.d.ts.map +0 -1
  373. package/dist/commands/validate.js +0 -70
  374. package/dist/commands/validate.js.map +0 -1
  375. package/dist/config/config.test.d.ts +0 -2
  376. package/dist/config/config.test.d.ts.map +0 -1
  377. package/dist/config/config.test.js +0 -164
  378. package/dist/config/config.test.js.map +0 -1
  379. package/dist/config/index.d.ts +0 -35
  380. package/dist/config/index.d.ts.map +0 -1
  381. package/dist/config/index.js +0 -101
  382. package/dist/config/index.js.map +0 -1
  383. package/dist/config/schema.d.ts +0 -52
  384. package/dist/config/schema.d.ts.map +0 -1
  385. package/dist/config/schema.js +0 -44
  386. package/dist/config/schema.js.map +0 -1
  387. package/dist/engine/index.d.ts +0 -3
  388. package/dist/engine/index.d.ts.map +0 -1
  389. package/dist/engine/index.js +0 -2
  390. package/dist/engine/index.js.map +0 -1
  391. package/dist/engine/project.d.ts +0 -84
  392. package/dist/engine/project.d.ts.map +0 -1
  393. package/dist/engine/project.js +0 -355
  394. package/dist/engine/project.js.map +0 -1
  395. package/dist/engine/project.test.d.ts +0 -2
  396. package/dist/engine/project.test.d.ts.map +0 -1
  397. package/dist/engine/project.test.js +0 -599
  398. package/dist/engine/project.test.js.map +0 -1
  399. package/dist/index.d.ts +0 -7
  400. package/dist/index.d.ts.map +0 -1
  401. package/dist/index.js.map +0 -1
  402. package/dist/pipeline/executor.d.ts +0 -19
  403. package/dist/pipeline/executor.d.ts.map +0 -1
  404. package/dist/pipeline/executor.js +0 -397
  405. package/dist/pipeline/executor.js.map +0 -1
  406. package/dist/pipeline/index.d.ts +0 -4
  407. package/dist/pipeline/index.d.ts.map +0 -1
  408. package/dist/pipeline/index.js +0 -2
  409. package/dist/pipeline/index.js.map +0 -1
  410. package/dist/pipeline/renderers/__tests__/html-template.test.d.ts +0 -2
  411. package/dist/pipeline/renderers/__tests__/html-template.test.d.ts.map +0 -1
  412. package/dist/pipeline/renderers/__tests__/html-template.test.js +0 -170
  413. package/dist/pipeline/renderers/__tests__/html-template.test.js.map +0 -1
  414. package/dist/pipeline/renderers/__tests__/integration.test.d.ts +0 -2
  415. package/dist/pipeline/renderers/__tests__/integration.test.d.ts.map +0 -1
  416. package/dist/pipeline/renderers/__tests__/integration.test.js +0 -175
  417. package/dist/pipeline/renderers/__tests__/integration.test.js.map +0 -1
  418. package/dist/pipeline/renderers/chrome.d.ts +0 -26
  419. package/dist/pipeline/renderers/chrome.d.ts.map +0 -1
  420. package/dist/pipeline/renderers/chrome.js +0 -48
  421. package/dist/pipeline/renderers/chrome.js.map +0 -1
  422. package/dist/pipeline/renderers/html-clips.d.ts +0 -20
  423. package/dist/pipeline/renderers/html-clips.d.ts.map +0 -1
  424. package/dist/pipeline/renderers/html-clips.js +0 -50
  425. package/dist/pipeline/renderers/html-clips.js.map +0 -1
  426. package/dist/pipeline/renderers/html-runtime.d.ts +0 -7
  427. package/dist/pipeline/renderers/html-runtime.d.ts.map +0 -1
  428. package/dist/pipeline/renderers/html-runtime.js +0 -80
  429. package/dist/pipeline/renderers/html-runtime.js.map +0 -1
  430. package/dist/pipeline/renderers/html-template.d.ts +0 -3
  431. package/dist/pipeline/renderers/html-template.d.ts.map +0 -1
  432. package/dist/pipeline/renderers/html-template.js +0 -36
  433. package/dist/pipeline/renderers/html-template.js.map +0 -1
  434. package/dist/pipeline/renderers/hyperframes.d.ts +0 -8
  435. package/dist/pipeline/renderers/hyperframes.d.ts.map +0 -1
  436. package/dist/pipeline/renderers/hyperframes.js +0 -63
  437. package/dist/pipeline/renderers/hyperframes.js.map +0 -1
  438. package/dist/pipeline/renderers/project-builder.d.ts +0 -8
  439. package/dist/pipeline/renderers/project-builder.d.ts.map +0 -1
  440. package/dist/pipeline/renderers/project-builder.js +0 -65
  441. package/dist/pipeline/renderers/project-builder.js.map +0 -1
  442. package/dist/pipeline/renderers/types.d.ts +0 -41
  443. package/dist/pipeline/renderers/types.d.ts.map +0 -1
  444. package/dist/pipeline/renderers/types.js +0 -11
  445. package/dist/pipeline/renderers/types.js.map +0 -1
  446. package/dist/pipeline/resolver.d.ts +0 -20
  447. package/dist/pipeline/resolver.d.ts.map +0 -1
  448. package/dist/pipeline/resolver.js +0 -74
  449. package/dist/pipeline/resolver.js.map +0 -1
  450. package/dist/pipeline/types.d.ts +0 -82
  451. package/dist/pipeline/types.d.ts.map +0 -1
  452. package/dist/pipeline/types.js +0 -8
  453. package/dist/pipeline/types.js.map +0 -1
  454. package/dist/utils/api-key.d.ts +0 -33
  455. package/dist/utils/api-key.d.ts.map +0 -1
  456. package/dist/utils/api-key.js +0 -228
  457. package/dist/utils/api-key.js.map +0 -1
  458. package/dist/utils/api-key.test.d.ts +0 -2
  459. package/dist/utils/api-key.test.d.ts.map +0 -1
  460. package/dist/utils/api-key.test.js +0 -35
  461. package/dist/utils/api-key.test.js.map +0 -1
  462. package/dist/utils/audio.d.ts +0 -23
  463. package/dist/utils/audio.d.ts.map +0 -1
  464. package/dist/utils/audio.js +0 -79
  465. package/dist/utils/audio.js.map +0 -1
  466. package/dist/utils/exec-safe.d.ts +0 -22
  467. package/dist/utils/exec-safe.d.ts.map +0 -1
  468. package/dist/utils/exec-safe.js +0 -62
  469. package/dist/utils/exec-safe.js.map +0 -1
  470. package/dist/utils/first-run.d.ts +0 -17
  471. package/dist/utils/first-run.d.ts.map +0 -1
  472. package/dist/utils/first-run.js +0 -73
  473. package/dist/utils/first-run.js.map +0 -1
  474. package/dist/utils/input-validation.d.ts +0 -27
  475. package/dist/utils/input-validation.d.ts.map +0 -1
  476. package/dist/utils/input-validation.js +0 -67
  477. package/dist/utils/input-validation.js.map +0 -1
  478. package/dist/utils/provider-resolver.d.ts +0 -19
  479. package/dist/utils/provider-resolver.d.ts.map +0 -1
  480. package/dist/utils/provider-resolver.js +0 -75
  481. package/dist/utils/provider-resolver.js.map +0 -1
  482. package/dist/utils/remotion.d.ts +0 -231
  483. package/dist/utils/remotion.d.ts.map +0 -1
  484. package/dist/utils/remotion.js +0 -879
  485. package/dist/utils/remotion.js.map +0 -1
  486. package/dist/utils/subtitle.d.ts +0 -65
  487. package/dist/utils/subtitle.d.ts.map +0 -1
  488. package/dist/utils/subtitle.js +0 -135
  489. package/dist/utils/subtitle.js.map +0 -1
  490. package/dist/utils/subtitle.test.d.ts +0 -2
  491. package/dist/utils/subtitle.test.d.ts.map +0 -1
  492. package/dist/utils/subtitle.test.js +0 -175
  493. package/dist/utils/subtitle.test.js.map +0 -1
  494. package/dist/utils/tty.d.ts +0 -45
  495. package/dist/utils/tty.d.ts.map +0 -1
  496. package/dist/utils/tty.js +0 -231
  497. package/dist/utils/tty.js.map +0 -1
@@ -1,980 +0,0 @@
1
- import { Command } from "commander";
2
- import { readFile, access, stat } from "node:fs/promises";
3
- import { resolve, basename } from "node:path";
4
- import { spawn } from "node:child_process";
5
- import chalk from "chalk";
6
- import ora from "ora";
7
- import { Project } from "../engine/index.js";
8
- import { execSafe, ffprobeDuration } from "../utils/exec-safe.js";
9
- import { exitWithError, generalError, notFoundError, outputResult, usageError } from "./output.js";
10
- import { validateOutputPath } from "./validate.js";
11
- /**
12
- * Resolve project file path - handles both file paths and directory paths
13
- * If path is a directory, looks for project.vibe.json inside
14
- */
15
- async function resolveProjectPath(inputPath) {
16
- const filePath = resolve(process.cwd(), inputPath);
17
- try {
18
- const stats = await stat(filePath);
19
- if (stats.isDirectory()) {
20
- return resolve(filePath, "project.vibe.json");
21
- }
22
- }
23
- catch {
24
- // Path doesn't exist or other error - let readFile handle it
25
- }
26
- return filePath;
27
- }
28
- /**
29
- * Get the duration of a media file using ffprobe
30
- * For images, returns a default duration since they have no inherent time
31
- */
32
- export async function getMediaDuration(filePath, mediaType, defaultImageDuration = 5) {
33
- if (mediaType === "image") {
34
- return defaultImageDuration;
35
- }
36
- try {
37
- return await ffprobeDuration(filePath);
38
- }
39
- catch {
40
- return defaultImageDuration;
41
- }
42
- }
43
- /**
44
- * Check if a media file has an audio stream
45
- */
46
- export async function checkHasAudio(filePath) {
47
- try {
48
- const { stdout } = await execSafe("ffprobe", [
49
- "-v", "error", "-select_streams", "a", "-show_entries", "stream=codec_type", "-of", "default=noprint_wrappers=1:nokey=1", filePath,
50
- ]);
51
- return stdout.trim().length > 0;
52
- }
53
- catch {
54
- return false;
55
- }
56
- }
57
- const CODEC_MAP = {
58
- h264: "libx264",
59
- h265: "libx265",
60
- vp9: "libvpx-vp9",
61
- };
62
- export const BITRATE_PATTERN = /^\d+(?:\.\d+)?[kKmMgG]?$/;
63
- export const RESOLUTION_PATTERN = /^\d+x\d+$/;
64
- export const VALID_CODECS = ["h264", "h265", "vp9"];
65
- export function validateOverrides(overrides) {
66
- if (overrides.bitrate !== undefined && !BITRATE_PATTERN.test(overrides.bitrate)) {
67
- return `Invalid --bitrate "${overrides.bitrate}". Use digits with optional k/M suffix (e.g. 5000k, 8M).`;
68
- }
69
- if (overrides.fps !== undefined) {
70
- if (!Number.isFinite(overrides.fps) || overrides.fps <= 0 || overrides.fps > 240) {
71
- return `Invalid --fps "${overrides.fps}". Use a positive number up to 240.`;
72
- }
73
- }
74
- if (overrides.resolution !== undefined && !RESOLUTION_PATTERN.test(overrides.resolution)) {
75
- return `Invalid --resolution "${overrides.resolution}". Use <width>x<height> (e.g. 1920x1080).`;
76
- }
77
- if (overrides.codec !== undefined && !VALID_CODECS.includes(overrides.codec)) {
78
- return `Invalid --codec "${overrides.codec}". Supported: ${VALID_CODECS.join(", ")}.`;
79
- }
80
- return null;
81
- }
82
- /**
83
- * Reusable export function for programmatic usage
84
- */
85
- export async function runExport(projectPath, outputPath, options = {}) {
86
- const { preset = "standard", format = "mp4", overwrite = false, gapFill = "extend", bitrate, fps, resolution, codec } = options;
87
- const overrideError = validateOverrides({ bitrate, fps, resolution, codec });
88
- if (overrideError) {
89
- return { success: false, message: overrideError };
90
- }
91
- try {
92
- // Check if FFmpeg is installed
93
- const ffmpegPath = await findFFmpeg();
94
- if (!ffmpegPath) {
95
- return {
96
- success: false,
97
- message: "FFmpeg not found. Install with: brew install ffmpeg (macOS) or apt install ffmpeg (Linux)",
98
- };
99
- }
100
- // Load project
101
- const filePath = await resolveProjectPath(projectPath);
102
- const content = await readFile(filePath, "utf-8");
103
- const data = JSON.parse(content);
104
- const project = Project.fromJSON(data);
105
- const summary = project.getSummary();
106
- if (summary.clipCount === 0) {
107
- return {
108
- success: false,
109
- message: "Project has no clips to export",
110
- };
111
- }
112
- // Determine output path
113
- const finalOutputPath = resolve(process.cwd(), outputPath);
114
- // Get preset settings
115
- const basePresetSettings = getPresetSettings(preset, summary.aspectRatio);
116
- const presetSettings = applyCustomOverrides(basePresetSettings, { bitrate, fps, resolution, codec });
117
- // Get clips sorted by start time
118
- const clips = project.getClips().sort((a, b) => a.startTime - b.startTime);
119
- const sources = project.getSources();
120
- // Verify source files exist, check audio streams, and measure actual durations
121
- const sourceAudioMap = new Map();
122
- const sourceActualDurationMap = new Map();
123
- for (const clip of clips) {
124
- const source = sources.find((s) => s.id === clip.sourceId);
125
- if (source) {
126
- try {
127
- await access(source.url);
128
- if (!sourceActualDurationMap.has(source.id)) {
129
- try {
130
- const dur = await getMediaDuration(source.url, source.type);
131
- if (dur > 0)
132
- sourceActualDurationMap.set(source.id, dur);
133
- }
134
- catch { /* fall back to metadata */ }
135
- }
136
- if (source.type === "video" && !sourceAudioMap.has(source.id)) {
137
- sourceAudioMap.set(source.id, await checkHasAudio(source.url));
138
- }
139
- }
140
- catch {
141
- return {
142
- success: false,
143
- message: `Source file not found: ${source.url}`,
144
- };
145
- }
146
- }
147
- }
148
- // Build FFmpeg command
149
- const ffmpegArgs = buildFFmpegArgs(clips, sources, presetSettings, finalOutputPath, { overwrite, format, gapFill }, sourceAudioMap, sourceActualDurationMap);
150
- // Run FFmpeg
151
- await runFFmpegProcess(ffmpegPath, ffmpegArgs, () => { });
152
- return {
153
- success: true,
154
- message: `Exported: ${outputPath}`,
155
- outputPath: finalOutputPath,
156
- };
157
- }
158
- catch (error) {
159
- const errorMessage = error instanceof Error ? error.message : String(error);
160
- return {
161
- success: false,
162
- message: `Export failed: ${errorMessage}`,
163
- };
164
- }
165
- }
166
- export const exportCommand = new Command("export")
167
- .description("Export project to video file")
168
- .argument("<project>", "Project file path")
169
- .option("-o, --output <path>", "Output file path")
170
- .option("-f, --format <format>", "Output format (mp4, webm, mov, gif)", "mp4")
171
- .option("-p, --preset <preset>", "Quality preset (draft, standard, high, ultra)", "standard")
172
- .option("-y, --overwrite", "Overwrite output file if exists", false)
173
- .option("-g, --gap-fill <strategy>", "Gap filling strategy (black, extend)", "extend")
174
- .option("--backend <name>", "Render backend: ffmpeg (default) | hyperframes (experimental)", "ffmpeg")
175
- .option("--bitrate <value>", "Video bitrate (e.g. 5000k, 8M) — overrides preset")
176
- .option("--fps <number>", "Frames per second (e.g. 24, 30, 60) — overrides preset")
177
- .option("--resolution <WxH>", "Output resolution (e.g. 1920x1080) — overrides preset")
178
- .option("--codec <codec>", "Video codec: h264 (default) | h265 | vp9 — overrides preset")
179
- .option("--dry-run", "Preview parameters without executing")
180
- .addHelpText("after", `
181
- Examples:
182
- $ vibe export project.vibe.json -o output.mp4
183
- $ vibe export project.vibe.json -o output.mp4 -p high -y
184
- $ vibe export project.vibe.json -o output.webm -f webm
185
- $ vibe export project.vibe.json -o output.gif -f gif
186
- $ vibe export project.vibe.json -o out.mp4 --bitrate 5000k --fps 24 --codec h265
187
- $ vibe export project.vibe.json -o out.mp4 -p high --fps 60
188
-
189
- Cost: Free (no API keys needed). Requires FFmpeg.
190
- GIF format: 15fps, no audio, looping. Good for previews and sharing.
191
- Custom flags (--bitrate, --fps, --resolution, --codec) override preset values.
192
- Run 'vibe schema export' for structured parameter info.`)
193
- .action(async (projectPath, options) => {
194
- const spinner = ora("Checking FFmpeg...").start();
195
- try {
196
- if (options.output) {
197
- validateOutputPath(options.output);
198
- }
199
- const customOverrides = {
200
- bitrate: options.bitrate,
201
- fps: options.fps !== undefined ? Number(options.fps) : undefined,
202
- resolution: options.resolution,
203
- codec: options.codec,
204
- };
205
- const overrideError = validateOverrides(customOverrides);
206
- if (overrideError) {
207
- spinner.stop();
208
- exitWithError(usageError(overrideError));
209
- }
210
- if (options.dryRun) {
211
- outputResult({
212
- dryRun: true,
213
- command: "export",
214
- params: {
215
- project: projectPath,
216
- output: options.output || null,
217
- format: options.format,
218
- preset: options.preset,
219
- overwrite: options.overwrite,
220
- gapFill: options.gapFill,
221
- backend: options.backend,
222
- bitrate: options.bitrate ?? null,
223
- fps: customOverrides.fps ?? null,
224
- resolution: options.resolution ?? null,
225
- codec: options.codec ?? null,
226
- },
227
- });
228
- return;
229
- }
230
- // Hyperframes backend path
231
- if (options.backend === "hyperframes") {
232
- await runHyperframesExport(projectPath, options, spinner);
233
- return;
234
- }
235
- // Check if FFmpeg is installed
236
- const ffmpegPath = await findFFmpeg();
237
- if (!ffmpegPath) {
238
- spinner.fail("FFmpeg not found");
239
- exitWithError(generalError("FFmpeg not found", "Install with: brew install ffmpeg (macOS), apt install ffmpeg (Linux), or winget install ffmpeg (Windows)"));
240
- }
241
- // Load project
242
- spinner.text = "Loading project...";
243
- const filePath = await resolveProjectPath(projectPath);
244
- const content = await readFile(filePath, "utf-8");
245
- const data = JSON.parse(content);
246
- const project = Project.fromJSON(data);
247
- const summary = project.getSummary();
248
- if (summary.clipCount === 0) {
249
- spinner.fail("Project has no clips to export");
250
- exitWithError(usageError("Project has no clips to export"));
251
- }
252
- // Determine output path
253
- const outputPath = options.output
254
- ? resolve(process.cwd(), options.output)
255
- : resolve(process.cwd(), `${basename(projectPath, ".vibe.json")}.${options.format}`);
256
- // Get preset settings + apply custom overrides
257
- const basePresetSettings = getPresetSettings(options.preset, summary.aspectRatio);
258
- const presetSettings = applyCustomOverrides(basePresetSettings, customOverrides);
259
- // Get clips sorted by start time
260
- const clips = project.getClips().sort((a, b) => a.startTime - b.startTime);
261
- const sources = project.getSources();
262
- // Verify source files exist, check audio streams, and measure actual durations
263
- spinner.text = "Verifying source files...";
264
- const sourceAudioMap = new Map();
265
- const sourceActualDurationMap = new Map();
266
- for (const clip of clips) {
267
- const source = sources.find((s) => s.id === clip.sourceId);
268
- if (source) {
269
- try {
270
- await access(source.url);
271
- if (!sourceActualDurationMap.has(source.id)) {
272
- try {
273
- const dur = await getMediaDuration(source.url, source.type);
274
- if (dur > 0)
275
- sourceActualDurationMap.set(source.id, dur);
276
- }
277
- catch { /* fall back to metadata */ }
278
- }
279
- if (source.type === "video" && !sourceAudioMap.has(source.id)) {
280
- sourceAudioMap.set(source.id, await checkHasAudio(source.url));
281
- }
282
- }
283
- catch {
284
- spinner.fail(`Source file not found: ${source.url}`);
285
- exitWithError(notFoundError(source.url));
286
- }
287
- }
288
- }
289
- // Build FFmpeg command
290
- spinner.text = "Building export command...";
291
- const gapFillStrategy = (options.gapFill === "black" ? "black" : "extend");
292
- const ffmpegArgs = buildFFmpegArgs(clips, sources, presetSettings, outputPath, { ...options, gapFill: gapFillStrategy }, sourceAudioMap, sourceActualDurationMap);
293
- if (process.env.DEBUG) {
294
- console.log("\nFFmpeg command:");
295
- console.log("ffmpeg", ffmpegArgs.join(" "));
296
- console.log();
297
- }
298
- // Run FFmpeg
299
- spinner.text = "Encoding...";
300
- await runFFmpegProcess(ffmpegPath, ffmpegArgs, (progress) => {
301
- spinner.text = `Encoding... ${progress}%`;
302
- });
303
- spinner.succeed(chalk.green(`Exported: ${outputPath}`));
304
- console.log();
305
- console.log(chalk.dim(" Duration:"), `${summary.duration.toFixed(1)}s`);
306
- console.log(chalk.dim(" Clips:"), summary.clipCount);
307
- console.log(chalk.dim(" Format:"), options.format);
308
- console.log(chalk.dim(" Preset:"), options.preset);
309
- console.log(chalk.dim(" Resolution:"), presetSettings.resolution);
310
- if (options.bitrate)
311
- console.log(chalk.dim(" Bitrate:"), options.bitrate);
312
- if (options.fps)
313
- console.log(chalk.dim(" FPS:"), options.fps);
314
- if (options.codec)
315
- console.log(chalk.dim(" Codec:"), options.codec);
316
- console.log();
317
- }
318
- catch (error) {
319
- spinner.fail("Export failed");
320
- const msg = error instanceof Error ? error.message : String(error);
321
- exitWithError(generalError(`Export failed: ${msg}`));
322
- }
323
- });
324
- /**
325
- * Find FFmpeg executable
326
- */
327
- async function findFFmpeg() {
328
- try {
329
- const { stdout } = await execSafe("which", ["ffmpeg"]);
330
- return stdout.trim().split("\n")[0];
331
- }
332
- catch {
333
- try {
334
- const { stdout } = await execSafe("where", ["ffmpeg"]);
335
- return stdout.trim().split("\n")[0];
336
- }
337
- catch {
338
- return null;
339
- }
340
- }
341
- }
342
- /**
343
- * Detect gaps in timeline between clips
344
- * Returns array of gaps with start and end times
345
- */
346
- function detectTimelineGaps(clips, totalDuration) {
347
- if (clips.length === 0)
348
- return [];
349
- const gaps = [];
350
- const sortedClips = [...clips].sort((a, b) => a.startTime - b.startTime);
351
- // Check for gap at the start (first clip doesn't start at 0)
352
- if (sortedClips[0].startTime > 0.001) {
353
- gaps.push({ start: 0, end: sortedClips[0].startTime });
354
- }
355
- // Check for gaps between clips
356
- for (let i = 0; i < sortedClips.length - 1; i++) {
357
- const clipEnd = sortedClips[i].startTime + sortedClips[i].duration;
358
- const nextStart = sortedClips[i + 1].startTime;
359
- // Allow small tolerance for floating point errors
360
- if (nextStart > clipEnd + 0.001) {
361
- gaps.push({ start: clipEnd, end: nextStart });
362
- }
363
- }
364
- // Check for gap at the end if totalDuration is provided
365
- if (totalDuration !== undefined) {
366
- const lastClip = sortedClips[sortedClips.length - 1];
367
- const lastClipEnd = lastClip.startTime + lastClip.duration;
368
- if (totalDuration > lastClipEnd + 0.001) {
369
- gaps.push({ start: lastClipEnd, end: totalDuration });
370
- }
371
- }
372
- return gaps;
373
- }
374
- /**
375
- * Create gap fill plans by extending adjacent clips
376
- * Priority:
377
- * 1. Extend clip AFTER the gap backwards (if sourceStartOffset > 0)
378
- * 2. Extend clip BEFORE the gap forwards (if source has unused duration)
379
- * 3. Fallback to black frames
380
- */
381
- function createGapFillPlans(gaps, clips, sources) {
382
- const sortedClips = [...clips].sort((a, b) => a.startTime - b.startTime);
383
- return gaps.map((gap) => {
384
- const fills = [];
385
- let remainingStart = gap.start;
386
- let remainingEnd = gap.end;
387
- // Find clip AFTER the gap (for extending backwards)
388
- const clipAfter = sortedClips.find((c) => Math.abs(c.startTime - gap.end) < 0.01);
389
- // Find clip BEFORE the gap (for extending forwards)
390
- const clipBefore = sortedClips.find((c) => Math.abs((c.startTime + c.duration) - gap.start) < 0.01);
391
- // Try extending clip after the gap backwards first
392
- if (clipAfter && clipAfter.sourceStartOffset > 0.01) {
393
- const source = sources.find((s) => s.id === clipAfter.sourceId);
394
- if (source && source.type === "video") {
395
- const availableExtension = clipAfter.sourceStartOffset;
396
- const extensionDuration = Math.min(availableExtension, remainingEnd - remainingStart);
397
- if (extensionDuration > 0.01) {
398
- // Extend from the gap end backwards
399
- const fillStart = remainingEnd - extensionDuration;
400
- const sourceStart = clipAfter.sourceStartOffset - extensionDuration;
401
- const sourceEnd = clipAfter.sourceStartOffset;
402
- fills.push({
403
- type: "extend-after",
404
- sourceId: source.id,
405
- sourceUrl: source.url,
406
- start: fillStart,
407
- end: remainingEnd,
408
- sourceStart,
409
- sourceEnd,
410
- });
411
- remainingEnd = fillStart;
412
- }
413
- }
414
- }
415
- // If there's still a gap, try extending clip before the gap forwards
416
- if (remainingEnd - remainingStart > 0.01 && clipBefore) {
417
- const source = sources.find((s) => s.id === clipBefore.sourceId);
418
- if (source && source.type === "video") {
419
- const usedEndInSource = clipBefore.sourceEndOffset;
420
- const availableExtension = source.duration - usedEndInSource;
421
- if (availableExtension > 0.01) {
422
- const extensionDuration = Math.min(availableExtension, remainingEnd - remainingStart);
423
- if (extensionDuration > 0.01) {
424
- const sourceStart = usedEndInSource;
425
- const sourceEnd = usedEndInSource + extensionDuration;
426
- fills.push({
427
- type: "extend-before",
428
- sourceId: source.id,
429
- sourceUrl: source.url,
430
- start: remainingStart,
431
- end: remainingStart + extensionDuration,
432
- sourceStart,
433
- sourceEnd,
434
- });
435
- remainingStart = remainingStart + extensionDuration;
436
- }
437
- }
438
- }
439
- }
440
- // Fill any remaining gap with black
441
- if (remainingEnd - remainingStart > 0.01) {
442
- fills.push({
443
- type: "black",
444
- start: remainingStart,
445
- end: remainingEnd,
446
- });
447
- }
448
- return { gap, fills };
449
- });
450
- }
451
- /**
452
- * Build FFmpeg arguments for export
453
- */
454
- function buildFFmpegArgs(clips, sources, presetSettings, outputPath, options, sourceAudioMap = new Map(), sourceActualDurationMap = new Map()) {
455
- const args = [];
456
- // Overwrite flag first
457
- if (options.overwrite) {
458
- args.push("-y");
459
- }
460
- // Add input files
461
- const sourceMap = new Map();
462
- let inputIndex = 0;
463
- for (const clip of clips) {
464
- const source = sources.find((s) => s.id === clip.sourceId);
465
- if (source && !sourceMap.has(source.id)) {
466
- // Add -loop 1 before image inputs to create a continuous video stream
467
- if (source.type === "image") {
468
- args.push("-loop", "1");
469
- }
470
- args.push("-i", source.url);
471
- sourceMap.set(source.id, inputIndex);
472
- inputIndex++;
473
- }
474
- }
475
- // Build filter complex
476
- const filterParts = [];
477
- // Separate clips by track type for proper timeline-based export
478
- // Get track info to determine clip types
479
- const videoClips = clips.filter((clip) => {
480
- const source = sources.find((s) => s.id === clip.sourceId);
481
- return source && (source.type === "image" || source.type === "video");
482
- }).sort((a, b) => a.startTime - b.startTime);
483
- // Include audio clips from:
484
- // 1. Explicit audio sources (narration, music)
485
- // 2. Video sources when there are NO separate audio clips (e.g., highlight reels)
486
- const explicitAudioClips = clips.filter((clip) => {
487
- const source = sources.find((s) => s.id === clip.sourceId);
488
- return source && source.type === "audio";
489
- }).sort((a, b) => a.startTime - b.startTime);
490
- // If no explicit audio clips, extract audio from video clips
491
- const audioClips = explicitAudioClips.length > 0
492
- ? explicitAudioClips
493
- : clips.filter((clip) => {
494
- const source = sources.find((s) => s.id === clip.sourceId);
495
- return source && source.type === "video";
496
- }).sort((a, b) => a.startTime - b.startTime);
497
- // Get target resolution for scaling (all clips must match for concat)
498
- const [targetWidth, targetHeight] = presetSettings.resolution.split("x").map(Number);
499
- // Detect gaps in video timeline
500
- // For totalDuration, use the longest audio clip end time if explicit audio exists
501
- // (audio is usually the reference for timing in b-roll scenarios)
502
- let totalDuration;
503
- if (explicitAudioClips.length > 0) {
504
- const audioEnd = Math.max(...explicitAudioClips.map(c => c.startTime + c.duration));
505
- totalDuration = audioEnd;
506
- }
507
- const videoGaps = detectTimelineGaps(videoClips, totalDuration);
508
- // Create gap fill plans based on strategy
509
- const gapFillStrategy = options.gapFill || "extend";
510
- const gapFillPlans = gapFillStrategy === "extend"
511
- ? createGapFillPlans(videoGaps, videoClips, sources)
512
- : videoGaps.map((gap) => ({
513
- gap,
514
- fills: [{ type: "black", start: gap.start, end: gap.end }],
515
- }));
516
- const videoSegments = [];
517
- // Add video clips as segments
518
- for (const clip of videoClips) {
519
- videoSegments.push({ type: 'clip', clip, startTime: clip.startTime });
520
- }
521
- // Add gap fills as segments (from gap fill plans)
522
- for (const plan of gapFillPlans) {
523
- for (const fill of plan.fills) {
524
- if (fill.type === "black") {
525
- videoSegments.push({
526
- type: 'black',
527
- startTime: fill.start,
528
- duration: fill.end - fill.start,
529
- });
530
- }
531
- else {
532
- // extend-before or extend-after
533
- videoSegments.push({
534
- type: 'extended',
535
- sourceId: fill.sourceId,
536
- sourceUrl: fill.sourceUrl,
537
- startTime: fill.start,
538
- duration: fill.end - fill.start,
539
- sourceStart: fill.sourceStart,
540
- sourceEnd: fill.sourceEnd,
541
- });
542
- }
543
- }
544
- }
545
- // Sort by start time
546
- videoSegments.sort((a, b) => a.startTime - b.startTime);
547
- // Process video segments (clips, extended clips, and black frames)
548
- const videoStreams = [];
549
- let videoStreamIdx = 0;
550
- for (const segment of videoSegments) {
551
- if (segment.type === 'clip' && segment.clip) {
552
- const clip = segment.clip;
553
- const source = sources.find((s) => s.id === clip.sourceId);
554
- if (!source)
555
- continue;
556
- const srcIdx = sourceMap.get(source.id);
557
- if (srcIdx === undefined)
558
- continue;
559
- // Video filter chain - images need different handling than video
560
- let videoFilter;
561
- if (source.type === "image") {
562
- // Images: trim from 0 to clip duration (no source offset since images are looped)
563
- videoFilter = `[${srcIdx}:v]trim=start=0:end=${clip.duration},setpts=PTS-STARTPTS`;
564
- }
565
- else {
566
- // Video: use source offsets
567
- const trimStart = clip.sourceStartOffset;
568
- const trimEnd = clip.sourceStartOffset + clip.duration;
569
- videoFilter = `[${srcIdx}:v]trim=start=${trimStart}:end=${trimEnd},setpts=PTS-STARTPTS`;
570
- // If video source is shorter than clip duration, freeze last frame to fill
571
- // This prevents black frames when narration is longer than generated video
572
- // Use actual measured duration (ffprobe) over project metadata (may be stale)
573
- const sourceDuration = sourceActualDurationMap.get(source.id) || source.duration || 0;
574
- const availableDuration = sourceDuration - clip.sourceStartOffset;
575
- if (availableDuration > 0 && availableDuration < clip.duration - 0.1) {
576
- const padDuration = clip.duration - availableDuration;
577
- videoFilter += `,tpad=stop_mode=clone:stop_duration=${padDuration.toFixed(3)}`;
578
- }
579
- }
580
- // Scale to target resolution for concat compatibility (force same size, pad if needed)
581
- videoFilter += `,scale=${targetWidth}:${targetHeight}:force_original_aspect_ratio=decrease,pad=${targetWidth}:${targetHeight}:(ow-iw)/2:(oh-ih)/2,setsar=1`;
582
- // Apply effects
583
- for (const effect of clip.effects || []) {
584
- if (effect.type === "fadeIn") {
585
- videoFilter += `,fade=t=in:st=0:d=${effect.duration}`;
586
- }
587
- else if (effect.type === "fadeOut") {
588
- const fadeStart = clip.duration - effect.duration;
589
- videoFilter += `,fade=t=out:st=${fadeStart}:d=${effect.duration}`;
590
- }
591
- }
592
- videoFilter += `[v${videoStreamIdx}]`;
593
- filterParts.push(videoFilter);
594
- videoStreams.push(`[v${videoStreamIdx}]`);
595
- videoStreamIdx++;
596
- }
597
- else if (segment.type === 'extended' && segment.sourceId) {
598
- // Extended segment - use source video to fill gap
599
- const srcIdx = sourceMap.get(segment.sourceId);
600
- if (srcIdx === undefined) {
601
- // Fallback to black if source not found in input map
602
- const gapFilter = `color=c=black:s=${targetWidth}x${targetHeight}:d=${segment.duration}:r=30,format=yuv420p[v${videoStreamIdx}]`;
603
- filterParts.push(gapFilter);
604
- videoStreams.push(`[v${videoStreamIdx}]`);
605
- videoStreamIdx++;
606
- continue;
607
- }
608
- const videoFilter = `[${srcIdx}:v]trim=start=${segment.sourceStart}:end=${segment.sourceEnd},setpts=PTS-STARTPTS,scale=${targetWidth}:${targetHeight}:force_original_aspect_ratio=decrease,pad=${targetWidth}:${targetHeight}:(ow-iw)/2:(oh-ih)/2,setsar=1[v${videoStreamIdx}]`;
609
- filterParts.push(videoFilter);
610
- videoStreams.push(`[v${videoStreamIdx}]`);
611
- videoStreamIdx++;
612
- }
613
- else if (segment.type === 'black') {
614
- // Generate black frame for the gap duration
615
- const gapFilter = `color=c=black:s=${targetWidth}x${targetHeight}:d=${segment.duration}:r=30,format=yuv420p[v${videoStreamIdx}]`;
616
- filterParts.push(gapFilter);
617
- videoStreams.push(`[v${videoStreamIdx}]`);
618
- videoStreamIdx++;
619
- }
620
- }
621
- // ── Multi-track audio processing ────────────────────────────────────
622
- // Group audio clips by trackId, build each track as a separate stream,
623
- // then mix all tracks together with amix.
624
- // Step 1: Group audio clips by track
625
- const audioTrackMap = new Map();
626
- for (const clip of audioClips) {
627
- const trackId = clip.trackId || "audio-track-1";
628
- if (!audioTrackMap.has(trackId)) {
629
- audioTrackMap.set(trackId, []);
630
- }
631
- audioTrackMap.get(trackId).push(clip);
632
- }
633
- // Compute total timeline duration for gap detection
634
- const videoDuration = videoClips.length > 0
635
- ? Math.max(...videoClips.map(c => c.startTime + c.duration))
636
- : 0;
637
- const audioDuration = audioClips.length > 0
638
- ? Math.max(...audioClips.map(c => c.startTime + c.duration))
639
- : 0;
640
- const timelineDuration = totalDuration || Math.max(videoDuration, audioDuration);
641
- // Step 2: Build each track as a concat stream
642
- const trackOutputLabels = [];
643
- let audioStreamIdx = 0;
644
- for (const [, trackClips] of audioTrackMap) {
645
- const sorted = [...trackClips].sort((a, b) => a.startTime - b.startTime);
646
- const trackGaps = detectTimelineGaps(sorted, timelineDuration);
647
- const segments = [];
648
- for (const clip of sorted) {
649
- segments.push({ type: 'clip', clip, startTime: clip.startTime });
650
- }
651
- for (const gap of trackGaps) {
652
- segments.push({ type: 'gap', gap, startTime: gap.start });
653
- }
654
- segments.sort((a, b) => a.startTime - b.startTime);
655
- // Process segments into filter chains
656
- const segmentLabels = [];
657
- for (const segment of segments) {
658
- if (segment.type === 'clip' && segment.clip) {
659
- const clip = segment.clip;
660
- const source = sources.find((s) => s.id === clip.sourceId);
661
- if (!source)
662
- continue;
663
- const srcIdx = sourceMap.get(source.id);
664
- if (srcIdx === undefined)
665
- continue;
666
- const hasAudio = source.type === "audio" || sourceAudioMap.get(source.id) === true;
667
- let audioFilter;
668
- if (hasAudio) {
669
- const audioTrimStart = clip.sourceStartOffset;
670
- const audioTrimEnd = clip.sourceStartOffset + clip.duration;
671
- const sourceDuration = source.duration || 0;
672
- const clipDuration = clip.duration;
673
- if (source.type === "audio" && sourceDuration > clipDuration && audioTrimStart === 0) {
674
- const tempo = sourceDuration / clipDuration;
675
- if (tempo <= 2.0) {
676
- audioFilter = `[${srcIdx}:a]atempo=${tempo.toFixed(4)},asetpts=PTS-STARTPTS`;
677
- }
678
- else {
679
- audioFilter = `[${srcIdx}:a]atrim=start=${audioTrimStart}:end=${audioTrimEnd},asetpts=PTS-STARTPTS`;
680
- }
681
- }
682
- else {
683
- audioFilter = `[${srcIdx}:a]atrim=start=${audioTrimStart}:end=${audioTrimEnd},asetpts=PTS-STARTPTS`;
684
- }
685
- }
686
- else {
687
- audioFilter = `anullsrc=r=48000:cl=stereo,atrim=0:${clip.duration},asetpts=PTS-STARTPTS`;
688
- }
689
- // Apply per-clip volume (0.0-1.0)
690
- const clipVolume = clip.volume;
691
- if (clipVolume !== undefined && clipVolume !== 1.0) {
692
- audioFilter += `,volume=${clipVolume.toFixed(2)}`;
693
- }
694
- // Apply audio effects (fade, volume effect)
695
- for (const effect of clip.effects || []) {
696
- if (effect.type === "fadeIn") {
697
- audioFilter += `,afade=t=in:st=0:d=${effect.duration}`;
698
- }
699
- else if (effect.type === "fadeOut") {
700
- const fadeStart = clip.duration - effect.duration;
701
- audioFilter += `,afade=t=out:st=${fadeStart}:d=${effect.duration}`;
702
- }
703
- else if (effect.type === "volume" && effect.params?.level !== undefined) {
704
- audioFilter += `,volume=${effect.params.level}`;
705
- }
706
- }
707
- const label = `a${audioStreamIdx}`;
708
- audioFilter += `[${label}]`;
709
- filterParts.push(audioFilter);
710
- segmentLabels.push(`[${label}]`);
711
- audioStreamIdx++;
712
- }
713
- else if (segment.type === 'gap' && segment.gap) {
714
- const gapDuration = segment.gap.end - segment.gap.start;
715
- if (gapDuration > 0.001) {
716
- const label = `a${audioStreamIdx}`;
717
- filterParts.push(`anullsrc=r=48000:cl=stereo,atrim=0:${gapDuration.toFixed(4)},asetpts=PTS-STARTPTS[${label}]`);
718
- segmentLabels.push(`[${label}]`);
719
- audioStreamIdx++;
720
- }
721
- }
722
- }
723
- // Concat this track's segments into one stream
724
- if (segmentLabels.length > 1) {
725
- const trackLabel = `atrack${trackOutputLabels.length}`;
726
- filterParts.push(`${segmentLabels.join("")}concat=n=${segmentLabels.length}:v=0:a=1[${trackLabel}]`);
727
- trackOutputLabels.push(`[${trackLabel}]`);
728
- }
729
- else if (segmentLabels.length === 1) {
730
- // Single segment — rename to track label
731
- const trackLabel = `atrack${trackOutputLabels.length}`;
732
- filterParts.push(`${segmentLabels[0]}acopy[${trackLabel}]`);
733
- trackOutputLabels.push(`[${trackLabel}]`);
734
- }
735
- }
736
- // Concatenate video clips
737
- if (videoStreams.length > 1) {
738
- filterParts.push(`${videoStreams.join("")}concat=n=${videoStreams.length}:v=1:a=0[outv]`);
739
- }
740
- else if (videoStreams.length === 1) {
741
- filterParts.push(`${videoStreams[0]}copy[outv]`);
742
- }
743
- // Step 3: Mix all audio tracks together
744
- // amix with normalize=0 prevents auto-volume reduction
745
- if (trackOutputLabels.length > 1) {
746
- filterParts.push(`${trackOutputLabels.join("")}amix=inputs=${trackOutputLabels.length}:duration=longest:normalize=0[outa]`);
747
- }
748
- else if (trackOutputLabels.length === 1) {
749
- filterParts.push(`${trackOutputLabels[0]}acopy[outa]`);
750
- }
751
- // Add filter complex
752
- args.push("-filter_complex", filterParts.join(";"));
753
- // Map outputs
754
- args.push("-map", "[outv]");
755
- if (trackOutputLabels.length > 0) {
756
- args.push("-map", "[outa]");
757
- }
758
- // Add encoding settings
759
- if (options.format === "gif") {
760
- // GIF: drop audio track (GIF has no audio)
761
- const audioIdx = args.indexOf("[outa]");
762
- if (audioIdx !== -1) {
763
- const mapIdx = args.lastIndexOf("-map", audioIdx);
764
- if (mapIdx !== -1)
765
- args.splice(mapIdx, 2);
766
- }
767
- args.push("-r", "15"); // 15fps for reasonable file size
768
- args.push("-loop", "0"); // loop forever
769
- }
770
- else {
771
- args.push(...presetSettings.ffmpegArgs);
772
- }
773
- // Output file
774
- args.push(outputPath);
775
- return args;
776
- }
777
- /**
778
- * Run FFmpeg with progress reporting
779
- */
780
- function runFFmpegProcess(ffmpegPath, args, onProgress) {
781
- return new Promise((resolve, reject) => {
782
- const ffmpeg = spawn(ffmpegPath, args, {
783
- stdio: ["pipe", "pipe", "pipe"],
784
- });
785
- let duration = 0;
786
- let stderr = "";
787
- ffmpeg.stderr?.on("data", (data) => {
788
- const output = data.toString();
789
- stderr += output;
790
- // Parse duration
791
- const durationMatch = output.match(/Duration: (\d+):(\d+):(\d+\.\d+)/);
792
- if (durationMatch) {
793
- const [, hours, minutes, seconds] = durationMatch;
794
- duration =
795
- parseInt(hours) * 3600 +
796
- parseInt(minutes) * 60 +
797
- parseFloat(seconds);
798
- }
799
- // Parse progress
800
- const timeMatch = output.match(/time=(\d+):(\d+):(\d+\.\d+)/);
801
- if (timeMatch && duration > 0) {
802
- const [, hours, minutes, seconds] = timeMatch;
803
- const currentTime = parseInt(hours) * 3600 +
804
- parseInt(minutes) * 60 +
805
- parseFloat(seconds);
806
- const percent = Math.min(100, Math.round((currentTime / duration) * 100));
807
- onProgress(percent);
808
- }
809
- });
810
- ffmpeg.on("close", (code) => {
811
- if (code === 0) {
812
- resolve();
813
- }
814
- else {
815
- // Extract error message
816
- const errorMatch = stderr.match(/Error.*$/m);
817
- const errorMsg = errorMatch ? errorMatch[0] : `FFmpeg exited with code ${code}`;
818
- reject(new Error(errorMsg));
819
- }
820
- });
821
- ffmpeg.on("error", (err) => {
822
- reject(err);
823
- });
824
- });
825
- }
826
- export function applyCustomOverrides(settings, overrides) {
827
- const result = {
828
- ...settings,
829
- ffmpegArgs: [...settings.ffmpegArgs],
830
- };
831
- if (overrides.resolution) {
832
- result.resolution = overrides.resolution;
833
- }
834
- if (overrides.codec) {
835
- const ffmpegCodec = CODEC_MAP[overrides.codec];
836
- const cvIdx = result.ffmpegArgs.indexOf("-c:v");
837
- if (cvIdx !== -1) {
838
- result.ffmpegArgs[cvIdx + 1] = ffmpegCodec;
839
- }
840
- else {
841
- result.ffmpegArgs.unshift("-c:v", ffmpegCodec);
842
- }
843
- }
844
- if (overrides.bitrate) {
845
- const crfIdx = result.ffmpegArgs.indexOf("-crf");
846
- if (crfIdx !== -1) {
847
- result.ffmpegArgs.splice(crfIdx, 2);
848
- }
849
- const bvIdx = result.ffmpegArgs.indexOf("-b:v");
850
- if (bvIdx !== -1) {
851
- result.ffmpegArgs[bvIdx + 1] = overrides.bitrate;
852
- }
853
- else {
854
- result.ffmpegArgs.push("-b:v", overrides.bitrate);
855
- }
856
- result.videoBitrate = overrides.bitrate;
857
- }
858
- if (overrides.fps !== undefined) {
859
- const rIdx = result.ffmpegArgs.indexOf("-r");
860
- const fpsValue = String(overrides.fps);
861
- if (rIdx !== -1) {
862
- result.ffmpegArgs[rIdx + 1] = fpsValue;
863
- }
864
- else {
865
- result.ffmpegArgs.push("-r", fpsValue);
866
- }
867
- }
868
- return result;
869
- }
870
- function getPresetSettings(preset, aspectRatio) {
871
- const presets = {
872
- draft: {
873
- resolution: "640x360",
874
- videoBitrate: "1M",
875
- audioBitrate: "128k",
876
- ffmpegArgs: [
877
- "-c:v", "libx264",
878
- "-preset", "ultrafast",
879
- "-crf", "28",
880
- "-c:a", "aac",
881
- "-b:a", "128k",
882
- ],
883
- },
884
- standard: {
885
- resolution: "1280x720",
886
- videoBitrate: "4M",
887
- audioBitrate: "192k",
888
- ffmpegArgs: [
889
- "-c:v", "libx264",
890
- "-preset", "medium",
891
- "-crf", "23",
892
- "-c:a", "aac",
893
- "-b:a", "192k",
894
- ],
895
- },
896
- high: {
897
- resolution: "1920x1080",
898
- videoBitrate: "8M",
899
- audioBitrate: "256k",
900
- ffmpegArgs: [
901
- "-c:v", "libx264",
902
- "-preset", "slow",
903
- "-crf", "18",
904
- "-c:a", "aac",
905
- "-b:a", "256k",
906
- ],
907
- },
908
- ultra: {
909
- resolution: "3840x2160",
910
- videoBitrate: "20M",
911
- audioBitrate: "320k",
912
- ffmpegArgs: [
913
- "-c:v", "libx264",
914
- "-preset", "slow",
915
- "-crf", "15",
916
- "-c:a", "aac",
917
- "-b:a", "320k",
918
- ],
919
- },
920
- };
921
- // Adjust resolution for aspect ratio
922
- const settings = { ...presets[preset] || presets.standard };
923
- if (aspectRatio === "9:16") {
924
- // Vertical video
925
- const [w, h] = settings.resolution.split("x");
926
- settings.resolution = `${h}x${w}`;
927
- }
928
- else if (aspectRatio === "1:1") {
929
- // Square video
930
- const h = settings.resolution.split("x")[1];
931
- settings.resolution = `${h}x${h}`;
932
- }
933
- return settings;
934
- }
935
- async function runHyperframesExport(projectPath, options, spinner) {
936
- spinner.text = "Loading project...";
937
- const { readFile } = await import("node:fs/promises");
938
- const { resolve, basename } = await import("node:path");
939
- const { Project } = await import("../engine/index.js");
940
- const { createHyperframesBackend } = await import("../pipeline/renderers/hyperframes.js");
941
- const { exitWithError, generalError, outputResult } = await import("./output.js");
942
- const chalk = (await import("chalk")).default;
943
- const filePath = resolve(process.cwd(), projectPath);
944
- const content = await readFile(filePath, "utf-8");
945
- const project = Project.fromJSON(JSON.parse(content));
946
- const state = project.getState();
947
- const outputPath = options.output
948
- ? resolve(process.cwd(), options.output)
949
- : resolve(process.cwd(), `${basename(projectPath, ".vibe.json")}.${options.format ?? "mp4"}`);
950
- const quality = (["draft", "standard", "high"].includes(options.preset ?? "")
951
- ? options.preset
952
- : "standard");
953
- const backend = createHyperframesBackend();
954
- spinner.text = "Rendering with Hyperframes...";
955
- const result = await backend.render({
956
- projectState: state,
957
- outputPath,
958
- fps: 30,
959
- quality,
960
- format: (options.format ?? "mp4"),
961
- onProgress: (pct, stage) => { spinner.text = `[hyperframes] ${stage} ${pct}%`; },
962
- });
963
- if (!result.success) {
964
- spinner.fail("Hyperframes render failed");
965
- exitWithError(generalError(`Hyperframes render failed: ${result.error}`));
966
- return;
967
- }
968
- spinner.succeed(chalk.green(`Exported: ${result.outputPath}`));
969
- outputResult({
970
- success: true,
971
- command: "export",
972
- result: {
973
- outputPath: result.outputPath,
974
- backend: "hyperframes",
975
- durationMs: result.durationMs,
976
- framesRendered: result.framesRendered,
977
- },
978
- });
979
- }
980
- //# sourceMappingURL=export.js.map