voxflow 1.15.0 → 1.15.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 (453) hide show
  1. package/README.md +34 -0
  2. package/bin/voxflow.js +27 -0
  3. package/dist/index.js +1 -1
  4. package/dist/remotion-bundle/02a2fb2eb80bc7bf.woff2 +0 -0
  5. package/dist/remotion-bundle/052ca5351e5e06ba.woff2 +0 -0
  6. package/dist/remotion-bundle/05853dd28f4019cb.woff2 +0 -0
  7. package/dist/remotion-bundle/072ead3737f7c0d0.woff2 +0 -0
  8. package/dist/remotion-bundle/07d4248613c86a2e.woff2 +0 -0
  9. package/dist/remotion-bundle/0884a5c2d1d2d99b.woff2 +0 -0
  10. package/dist/remotion-bundle/0b0e185b2752095e.woff2 +0 -0
  11. package/dist/remotion-bundle/0e66c11bde067d91.woff2 +0 -0
  12. package/dist/remotion-bundle/0f7794cfba2c5d21.woff2 +0 -0
  13. package/dist/remotion-bundle/0fdbae5a4365783a.woff2 +0 -0
  14. package/dist/remotion-bundle/112.bundle.js +11 -0
  15. package/dist/remotion-bundle/112.bundle.js.map +1 -0
  16. package/dist/remotion-bundle/113.bundle.js +11 -0
  17. package/dist/remotion-bundle/113.bundle.js.map +1 -0
  18. package/dist/remotion-bundle/119cae0c4c16f7ed.woff2 +0 -0
  19. package/dist/remotion-bundle/14725f649fd1e78c.woff2 +0 -0
  20. package/dist/remotion-bundle/14abe9e3f95f7888.woff2 +0 -0
  21. package/dist/remotion-bundle/163.bundle.js +14678 -0
  22. package/dist/remotion-bundle/163.bundle.js.map +1 -0
  23. package/dist/remotion-bundle/1808c54072bf6d14.woff2 +0 -0
  24. package/dist/remotion-bundle/18948bec3e3012fe.woff2 +0 -0
  25. package/dist/remotion-bundle/1a661c60d0fc84fc.woff2 +0 -0
  26. package/dist/remotion-bundle/1af94941e1bc7e1e.woff2 +0 -0
  27. package/dist/remotion-bundle/1bee0219595f606c.woff2 +0 -0
  28. package/dist/remotion-bundle/1bfd5da7ce9d4ec4.woff2 +0 -0
  29. package/dist/remotion-bundle/1c158d56f1884f3f.woff2 +0 -0
  30. package/dist/remotion-bundle/1cf5e88e667610eb.woff2 +0 -0
  31. package/dist/remotion-bundle/1d431bd10f53c481.woff2 +0 -0
  32. package/dist/remotion-bundle/1d701a81a7670db2.woff2 +0 -0
  33. package/dist/remotion-bundle/1da0fecad4240f16.woff2 +0 -0
  34. package/dist/remotion-bundle/1ed14d3d0c5c63fe.woff2 +0 -0
  35. package/dist/remotion-bundle/1edfecf40e586f53.woff2 +0 -0
  36. package/dist/remotion-bundle/1f479711bc34b054.woff +0 -0
  37. package/dist/remotion-bundle/1f86e54a0ff5fcd1.woff2 +0 -0
  38. package/dist/remotion-bundle/2043ea87d9aabd11.woff2 +0 -0
  39. package/dist/remotion-bundle/20563c39ee8a0e40.woff2 +0 -0
  40. package/dist/remotion-bundle/20c231590fd12c44.woff2 +0 -0
  41. package/dist/remotion-bundle/20ce61713f754c07.woff2 +0 -0
  42. package/dist/remotion-bundle/21eb9306fce24bb1.woff2 +0 -0
  43. package/dist/remotion-bundle/244bf71c0cc851af.woff2 +0 -0
  44. package/dist/remotion-bundle/274d4cfc02bffbcb.woff2 +0 -0
  45. package/dist/remotion-bundle/275.bundle.js +21 -0
  46. package/dist/remotion-bundle/275.bundle.js.map +1 -0
  47. package/dist/remotion-bundle/2958f540b39513dc.woff2 +0 -0
  48. package/dist/remotion-bundle/2a168b98fd97722e.woff2 +0 -0
  49. package/dist/remotion-bundle/2d1f6373937ab55f.woff2 +0 -0
  50. package/dist/remotion-bundle/2d213ae47ff6daa9.woff2 +0 -0
  51. package/dist/remotion-bundle/2e4b1f04fcd05047.woff2 +0 -0
  52. package/dist/remotion-bundle/304170d98f4c4563.woff2 +0 -0
  53. package/dist/remotion-bundle/30d02e136e7a5642.woff2 +0 -0
  54. package/dist/remotion-bundle/3135562b52a714cd.woff2 +0 -0
  55. package/dist/remotion-bundle/313713af2c8144e9.woff2 +0 -0
  56. package/dist/remotion-bundle/325fa4108d2285b9.woff2 +0 -0
  57. package/dist/remotion-bundle/338e927ed3345e0c.woff2 +0 -0
  58. package/dist/remotion-bundle/35fc6b190365bc17.woff2 +0 -0
  59. package/dist/remotion-bundle/37a51f1122d4efc5.woff2 +0 -0
  60. package/dist/remotion-bundle/39a4d63e02736f5e.woff2 +0 -0
  61. package/dist/remotion-bundle/3a00e0d62dfc4171.woff2 +0 -0
  62. package/dist/remotion-bundle/3a6955e6561affe1.woff2 +0 -0
  63. package/dist/remotion-bundle/3c573945aef49b89.woff2 +0 -0
  64. package/dist/remotion-bundle/3cdbfbfa23b516a5.woff2 +0 -0
  65. package/dist/remotion-bundle/3e42f85a9e64ca8a.woff2 +0 -0
  66. package/dist/remotion-bundle/3e83eaf1ec859415.woff2 +0 -0
  67. package/dist/remotion-bundle/3f3c8c90de1250ee.woff2 +0 -0
  68. package/dist/remotion-bundle/434.bundle.js +205 -0
  69. package/dist/remotion-bundle/434.bundle.js.map +1 -0
  70. package/dist/remotion-bundle/44ffc6ca4d781692.woff2 +0 -0
  71. package/dist/remotion-bundle/4670d9c4580b09eb.woff2 +0 -0
  72. package/dist/remotion-bundle/479756881b302824.woff2 +0 -0
  73. package/dist/remotion-bundle/481b82134bfa9c82.woff2 +0 -0
  74. package/dist/remotion-bundle/48d27029626f4328.woff2 +0 -0
  75. package/dist/remotion-bundle/49b7b2a30329c511.woff2 +0 -0
  76. package/dist/remotion-bundle/4c8b25a1a9337045.woff2 +0 -0
  77. package/dist/remotion-bundle/4cba14788ca9259b.woff2 +0 -0
  78. package/dist/remotion-bundle/4cd6c589c004a6a7.woff2 +0 -0
  79. package/dist/remotion-bundle/4cd8d79c1021608d.woff2 +0 -0
  80. package/dist/remotion-bundle/4d8fa99b3f00f9f0.woff2 +0 -0
  81. package/dist/remotion-bundle/4e7805a643f86d53.woff2 +0 -0
  82. package/dist/remotion-bundle/4ff91be454542e3f.woff2 +0 -0
  83. package/dist/remotion-bundle/504cbcba1f63591b.woff2 +0 -0
  84. package/dist/remotion-bundle/5202d792e5791d6c.woff2 +0 -0
  85. package/dist/remotion-bundle/534db5ad4770cc1d.woff2 +0 -0
  86. package/dist/remotion-bundle/53b9568eb85f866b.woff2 +0 -0
  87. package/dist/remotion-bundle/543ad386ca171de9.woff2 +0 -0
  88. package/dist/remotion-bundle/54798e55bbf7976e.woff2 +0 -0
  89. package/dist/remotion-bundle/580.bundle.js +11 -0
  90. package/dist/remotion-bundle/580.bundle.js.map +1 -0
  91. package/dist/remotion-bundle/58d174d1193af6d1.woff2 +0 -0
  92. package/dist/remotion-bundle/591d29ff3ff53c80.woff2 +0 -0
  93. package/dist/remotion-bundle/5c28c4f4824383c6.woff2 +0 -0
  94. package/dist/remotion-bundle/5da9740d2ce894c8.woff2 +0 -0
  95. package/dist/remotion-bundle/6197735364642360.woff2 +0 -0
  96. package/dist/remotion-bundle/6265a4335724080f.woff2 +0 -0
  97. package/dist/remotion-bundle/633f5e4f6394daa7.woff2 +0 -0
  98. package/dist/remotion-bundle/637d95ace6a69c49.woff2 +0 -0
  99. package/dist/remotion-bundle/648e04a04dacff8f.woff2 +0 -0
  100. package/dist/remotion-bundle/64a6e83045a008b2.woff2 +0 -0
  101. package/dist/remotion-bundle/651.bundle.js +11 -0
  102. package/dist/remotion-bundle/651.bundle.js.map +1 -0
  103. package/dist/remotion-bundle/65e2a988c070facc.woff2 +0 -0
  104. package/dist/remotion-bundle/66a2f6ce5cc69105.woff2 +0 -0
  105. package/dist/remotion-bundle/690.bundle.js +3479 -0
  106. package/dist/remotion-bundle/690.bundle.js.map +1 -0
  107. package/dist/remotion-bundle/690ff55252ca715d.woff2 +0 -0
  108. package/dist/remotion-bundle/6a01a1cff49314fc.woff2 +0 -0
  109. package/dist/remotion-bundle/6cbc32670982986c.woff2 +0 -0
  110. package/dist/remotion-bundle/6d3cc42ae547f454.woff2 +0 -0
  111. package/dist/remotion-bundle/6d8f4cfa1ddc0830.woff2 +0 -0
  112. package/dist/remotion-bundle/6e4d7c6ae65e2dc3.woff2 +0 -0
  113. package/dist/remotion-bundle/6e86418bbcefb2e8.woff2 +0 -0
  114. package/dist/remotion-bundle/6ee02884b29cf7fb.woff2 +0 -0
  115. package/dist/remotion-bundle/6f436a74c9e3252c.woff2 +0 -0
  116. package/dist/remotion-bundle/78c8022f1657618b.woff2 +0 -0
  117. package/dist/remotion-bundle/7c5444169792bca4.woff2 +0 -0
  118. package/dist/remotion-bundle/7c86bddd9d997212.woff2 +0 -0
  119. package/dist/remotion-bundle/7e1284684767f584.woff2 +0 -0
  120. package/dist/remotion-bundle/7e81c17522d182b2.woff2 +0 -0
  121. package/dist/remotion-bundle/7eb87be198f7858c.woff2 +0 -0
  122. package/dist/remotion-bundle/8060c928f948aab5.woff2 +0 -0
  123. package/dist/remotion-bundle/80bc9dfbea2b35ae.woff2 +0 -0
  124. package/dist/remotion-bundle/811b83f69963bb48.woff2 +0 -0
  125. package/dist/remotion-bundle/813.bundle.js +117511 -0
  126. package/dist/remotion-bundle/813.bundle.js.map +1 -0
  127. package/dist/remotion-bundle/84df492e349f82e9.woff2 +0 -0
  128. package/dist/remotion-bundle/8501bfd73eb36f2b.woff2 +0 -0
  129. package/dist/remotion-bundle/854236a8376093fe.woff2 +0 -0
  130. package/dist/remotion-bundle/8571d74529082753.woff2 +0 -0
  131. package/dist/remotion-bundle/860bf44f8e6f4b5d.woff2 +0 -0
  132. package/dist/remotion-bundle/879.bundle.js +64 -0
  133. package/dist/remotion-bundle/879.bundle.js.map +1 -0
  134. package/dist/remotion-bundle/887dd482f848d56f.woff2 +0 -0
  135. package/dist/remotion-bundle/89b2132e85fbbb5a.woff2 +0 -0
  136. package/dist/remotion-bundle/8ba60d6c306010c2.woff2 +0 -0
  137. package/dist/remotion-bundle/8c7c4dadea897806.woff2 +0 -0
  138. package/dist/remotion-bundle/8c943f9999706f61.woff2 +0 -0
  139. package/dist/remotion-bundle/8f2a718c90575cc9.woff2 +0 -0
  140. package/dist/remotion-bundle/906b6edb3e1772c9.woff2 +0 -0
  141. package/dist/remotion-bundle/930ff9daccdf14eb.woff2 +0 -0
  142. package/dist/remotion-bundle/934db2f1c403c4d0.woff2 +0 -0
  143. package/dist/remotion-bundle/938.bundle.js +451 -0
  144. package/dist/remotion-bundle/938.bundle.js.map +1 -0
  145. package/dist/remotion-bundle/967.bundle.js +4462 -0
  146. package/dist/remotion-bundle/967.bundle.js.map +1 -0
  147. package/dist/remotion-bundle/9684a1093d3c02ce.woff2 +0 -0
  148. package/dist/remotion-bundle/973dcd0faa6116cc.woff2 +0 -0
  149. package/dist/remotion-bundle/9745400694e76cd8.woff2 +0 -0
  150. package/dist/remotion-bundle/999ef957bed3bdca.woff2 +0 -0
  151. package/dist/remotion-bundle/99a3d67c8b0f43e3.woff2 +0 -0
  152. package/dist/remotion-bundle/a0586c3e03127283.woff2 +0 -0
  153. package/dist/remotion-bundle/a0eb654fdae46269.woff2 +0 -0
  154. package/dist/remotion-bundle/a20e35d3b08f7994.woff2 +0 -0
  155. package/dist/remotion-bundle/a2dcaced7c8c25ab.woff2 +0 -0
  156. package/dist/remotion-bundle/a79255a972a2681a.woff2 +0 -0
  157. package/dist/remotion-bundle/a804b352cb9fec1a.woff2 +0 -0
  158. package/dist/remotion-bundle/aae7117164e1eabc.woff2 +0 -0
  159. package/dist/remotion-bundle/affd121385d0442d.woff2 +0 -0
  160. package/dist/remotion-bundle/b19a6083987ee0d7.woff2 +0 -0
  161. package/dist/remotion-bundle/b1b2bd04d8637981.woff2 +0 -0
  162. package/dist/remotion-bundle/b2c07f341486be87.woff2 +0 -0
  163. package/dist/remotion-bundle/b33d8f82e575c4ce.woff2 +0 -0
  164. package/dist/remotion-bundle/b366c0bed35ef491.woff2 +0 -0
  165. package/dist/remotion-bundle/b41e857ec1b85642.woff2 +0 -0
  166. package/dist/remotion-bundle/b420bb34ccf23e7f.woff2 +0 -0
  167. package/dist/remotion-bundle/b4f7bf4efb0c0ccf.woff2 +0 -0
  168. package/dist/remotion-bundle/b60fe5eca03cff93.woff2 +0 -0
  169. package/dist/remotion-bundle/b6bd31a336e64bce.woff2 +0 -0
  170. package/dist/remotion-bundle/b6d2befba3dfefeb.woff2 +0 -0
  171. package/dist/remotion-bundle/b75f39ab06c43bf4.woff2 +0 -0
  172. package/dist/remotion-bundle/b77880e8c413d4fd.woff2 +0 -0
  173. package/dist/remotion-bundle/b7e38ec441e4a77a.woff2 +0 -0
  174. package/dist/remotion-bundle/b83baa383ff0bf2b.woff2 +0 -0
  175. package/dist/remotion-bundle/b9ad7b6c0a11450a.woff2 +0 -0
  176. package/dist/remotion-bundle/baf84486e8ae3aaf.woff2 +0 -0
  177. package/dist/remotion-bundle/bc047b1f6869cffa.woff2 +0 -0
  178. package/dist/remotion-bundle/bf4f3ac6e93f33aa.woff2 +0 -0
  179. package/dist/remotion-bundle/bf6835ffec5897a2.woff2 +0 -0
  180. package/dist/remotion-bundle/bf8885f581eb1724.woff2 +0 -0
  181. package/dist/remotion-bundle/bundle.js +83376 -0
  182. package/dist/remotion-bundle/bundle.js.map +1 -0
  183. package/dist/remotion-bundle/c03f046bccd789d0.woff2 +0 -0
  184. package/dist/remotion-bundle/c0bb1f8962b73bc3.woff2 +0 -0
  185. package/dist/remotion-bundle/c1003f9a7db6e1cf.woff2 +0 -0
  186. package/dist/remotion-bundle/c15d83fb1e199515.woff2 +0 -0
  187. package/dist/remotion-bundle/c28e7e5d310f73ef.woff2 +0 -0
  188. package/dist/remotion-bundle/c2b840274db78aea.woff2 +0 -0
  189. package/dist/remotion-bundle/c3000e3299d4e45f.woff2 +0 -0
  190. package/dist/remotion-bundle/c83ce886e5288510.woff2 +0 -0
  191. package/dist/remotion-bundle/c87a5a64d4ac0918.woff2 +0 -0
  192. package/dist/remotion-bundle/c8a7e0d049e965fa.woff2 +0 -0
  193. package/dist/remotion-bundle/c949a35d3a3b1faf.woff2 +0 -0
  194. package/dist/remotion-bundle/c9618c9b9ac2bc78.woff2 +0 -0
  195. package/dist/remotion-bundle/ca3add3b84152d5b.woff2 +0 -0
  196. package/dist/remotion-bundle/cad9dd036408d707.woff2 +0 -0
  197. package/dist/remotion-bundle/cbb24916619df439.woff2 +0 -0
  198. package/dist/remotion-bundle/cc054f0b5514e177.woff2 +0 -0
  199. package/dist/remotion-bundle/ccc248ed9312bc71.woff2 +0 -0
  200. package/dist/remotion-bundle/cd9d623aa07af925.woff2 +0 -0
  201. package/dist/remotion-bundle/ce2ba7a321bd1247.woff2 +0 -0
  202. package/dist/remotion-bundle/cf72455f79a29b14.woff2 +0 -0
  203. package/dist/remotion-bundle/d267cbfefab452ac.woff2 +0 -0
  204. package/dist/remotion-bundle/d435cff46a64955f.woff +0 -0
  205. package/dist/remotion-bundle/d494d07f67e363f6.woff2 +0 -0
  206. package/dist/remotion-bundle/d7aa0cc1fa47bf38.woff2 +0 -0
  207. package/dist/remotion-bundle/d7c5ca93d885160a.woff2 +0 -0
  208. package/dist/remotion-bundle/d855d3e252db74e2.woff2 +0 -0
  209. package/dist/remotion-bundle/d8f13d47f02f82c2.woff2 +0 -0
  210. package/dist/remotion-bundle/d9567cce2ee11019.woff2 +0 -0
  211. package/dist/remotion-bundle/db8d4456fc75dd86.woff +0 -0
  212. package/dist/remotion-bundle/dc274628378c47ee.woff2 +0 -0
  213. package/dist/remotion-bundle/dc3e06947bb69903.woff2 +0 -0
  214. package/dist/remotion-bundle/dd67040ac3b6d523.woff2 +0 -0
  215. package/dist/remotion-bundle/e0b04bd488f953f4.woff2 +0 -0
  216. package/dist/remotion-bundle/e2a572ff95089370.woff2 +0 -0
  217. package/dist/remotion-bundle/e2e18a86b1c2b0cc.woff2 +0 -0
  218. package/dist/remotion-bundle/e3a78ee2fc9c6931.woff2 +0 -0
  219. package/dist/remotion-bundle/e654c9d547605a9f.woff2 +0 -0
  220. package/dist/remotion-bundle/e67a3a64c129927c.woff2 +0 -0
  221. package/dist/remotion-bundle/e6be28b4203cd6ce.woff2 +0 -0
  222. package/dist/remotion-bundle/e841907ad9b0a191.woff +0 -0
  223. package/dist/remotion-bundle/e889d1541c69fffa.woff2 +0 -0
  224. package/dist/remotion-bundle/e88ef8c76373a9e2.woff2 +0 -0
  225. package/dist/remotion-bundle/e9c72f4bc37defef.woff2 +0 -0
  226. package/dist/remotion-bundle/e9e35f863403a255.woff2 +0 -0
  227. package/dist/remotion-bundle/eb23b37b009375da.woff2 +0 -0
  228. package/dist/remotion-bundle/ee1342b741625721.woff2 +0 -0
  229. package/dist/remotion-bundle/f07da88543a57ec9.woff2 +0 -0
  230. package/dist/remotion-bundle/f522982115306f8a.woff2 +0 -0
  231. package/dist/remotion-bundle/f8449bd864e6d8bc.woff2 +0 -0
  232. package/dist/remotion-bundle/f906dd5bd95ff9ab.woff2 +0 -0
  233. package/dist/remotion-bundle/f9e9e9413e3c38bb.woff2 +0 -0
  234. package/dist/remotion-bundle/fa5a5b16280994a8.woff2 +0 -0
  235. package/dist/remotion-bundle/favicon.ico +0 -0
  236. package/dist/remotion-bundle/fb19c0517725599b.woff2 +0 -0
  237. package/dist/remotion-bundle/fcaf24232f684b9b.woff2 +0 -0
  238. package/dist/remotion-bundle/fe09e084a3eea8cf.woff2 +0 -0
  239. package/dist/remotion-bundle/ff38d5317df7345a.woff2 +0 -0
  240. package/dist/remotion-bundle/ffe7ea1ea08f455a.woff2 +0 -0
  241. package/dist/remotion-bundle/index.html +49 -0
  242. package/dist/remotion-bundle/public/paper-slide/female-kefu-xiaomei/communication/0.mp3 +0 -0
  243. package/dist/remotion-bundle/public/paper-slide/female-kefu-xiaomei/communication/1.mp3 +0 -0
  244. package/dist/remotion-bundle/public/paper-slide/female-kefu-xiaomei/communication/2.mp3 +0 -0
  245. package/dist/remotion-bundle/public/paper-slide/female-kefu-xiaomei/communication/3.mp3 +0 -0
  246. package/dist/remotion-bundle/public/paper-slide/female-kefu-xiaoxin/career/0.mp3 +0 -0
  247. package/dist/remotion-bundle/public/paper-slide/female-kefu-xiaoxin/career/1.mp3 +0 -0
  248. package/dist/remotion-bundle/public/paper-slide/female-kefu-xiaoxin/career/2.mp3 +0 -0
  249. package/dist/remotion-bundle/public/paper-slide/female-kefu-xiaoxin/career/3.mp3 +0 -0
  250. package/dist/remotion-bundle/public/paper-slide/female-kefu-xiaoyue/parenting/0.mp3 +0 -0
  251. package/dist/remotion-bundle/public/paper-slide/female-kefu-xiaoyue/parenting/1.mp3 +0 -0
  252. package/dist/remotion-bundle/public/paper-slide/female-kefu-xiaoyue/parenting/2.mp3 +0 -0
  253. package/dist/remotion-bundle/public/paper-slide/female-kefu-xiaoyue/parenting/3.mp3 +0 -0
  254. package/dist/remotion-bundle/public/paper-slide/male-kefu-xiaoxu/time-trap/0.mp3 +0 -0
  255. package/dist/remotion-bundle/public/paper-slide/male-kefu-xiaoxu/time-trap/1.mp3 +0 -0
  256. package/dist/remotion-bundle/public/paper-slide/male-kefu-xiaoxu/time-trap/2.mp3 +0 -0
  257. package/dist/remotion-bundle/public/paper-slide/male-kefu-xiaoxu/time-trap/3.mp3 +0 -0
  258. package/dist/remotion-bundle/public/paper-slide/v-female-A6b7WpG3/cognition/0.mp3 +0 -0
  259. package/dist/remotion-bundle/public/paper-slide/v-female-A6b7WpG3/cognition/1.mp3 +0 -0
  260. package/dist/remotion-bundle/public/paper-slide/v-female-A6b7WpG3/cognition/2.mp3 +0 -0
  261. package/dist/remotion-bundle/public/paper-slide/v-female-A6b7WpG3/cognition/3.mp3 +0 -0
  262. package/dist/remotion-bundle/public/paper-slide/v-female-A6b7WpG3/growth/0.mp3 +0 -0
  263. package/dist/remotion-bundle/public/paper-slide/v-female-A6b7WpG3/growth/1.mp3 +0 -0
  264. package/dist/remotion-bundle/public/paper-slide/v-female-A6b7WpG3/growth/2.mp3 +0 -0
  265. package/dist/remotion-bundle/public/paper-slide/v-female-A6b7WpG3/growth/3.mp3 +0 -0
  266. package/dist/remotion-bundle/public/paper-slide/v-female-A6b7WpG3/parenting/0.mp3 +0 -0
  267. package/dist/remotion-bundle/public/paper-slide/v-female-A6b7WpG3/parenting/1.mp3 +0 -0
  268. package/dist/remotion-bundle/public/paper-slide/v-female-A6b7WpG3/parenting/2.mp3 +0 -0
  269. package/dist/remotion-bundle/public/paper-slide/v-female-A6b7WpG3/parenting/3.mp3 +0 -0
  270. package/dist/remotion-bundle/public/paper-slide/v-female-A6b7WpG3/soothing/0.mp3 +0 -0
  271. package/dist/remotion-bundle/public/paper-slide/v-female-A6b7WpG3/soothing/1.mp3 +0 -0
  272. package/dist/remotion-bundle/public/paper-slide/v-female-A6b7WpG3/soothing/2.mp3 +0 -0
  273. package/dist/remotion-bundle/public/paper-slide/v-female-A6b7WpG3/soothing/3.mp3 +0 -0
  274. package/dist/remotion-bundle/public/paper-slide/v-female-R2s4N9qJ/cognition/0.mp3 +0 -0
  275. package/dist/remotion-bundle/public/paper-slide/v-female-R2s4N9qJ/cognition/1.mp3 +0 -0
  276. package/dist/remotion-bundle/public/paper-slide/v-female-R2s4N9qJ/cognition/2.mp3 +0 -0
  277. package/dist/remotion-bundle/public/paper-slide/v-female-R2s4N9qJ/cognition/3.mp3 +0 -0
  278. package/dist/remotion-bundle/public/paper-slide/v-male-Bk7vD3xP/decision/0.mp3 +0 -0
  279. package/dist/remotion-bundle/public/paper-slide/v-male-Bk7vD3xP/decision/1.mp3 +0 -0
  280. package/dist/remotion-bundle/public/paper-slide/v-male-Bk7vD3xP/decision/2.mp3 +0 -0
  281. package/dist/remotion-bundle/public/paper-slide/v-male-Bk7vD3xP/decision/3.mp3 +0 -0
  282. package/dist/remotion-bundle/public/paper-slide/v-male-W1tH9jVc/manager/0.mp3 +0 -0
  283. package/dist/remotion-bundle/public/paper-slide/v-male-W1tH9jVc/manager/1.mp3 +0 -0
  284. package/dist/remotion-bundle/public/paper-slide/v-male-W1tH9jVc/manager/2.mp3 +0 -0
  285. package/dist/remotion-bundle/public/paper-slide/v-male-W1tH9jVc/manager/3.mp3 +0 -0
  286. package/dist/remotion-bundle/public/paper-slide/v-male-W1tH9jVc/manager/4.mp3 +0 -0
  287. package/dist/remotion-bundle/public/paper-slide/v-male-s5NqE0rZ/founder/0.mp3 +0 -0
  288. package/dist/remotion-bundle/public/paper-slide/v-male-s5NqE0rZ/founder/1.mp3 +0 -0
  289. package/dist/remotion-bundle/public/paper-slide/v-male-s5NqE0rZ/founder/2.mp3 +0 -0
  290. package/dist/remotion-bundle/public/paper-slide/v-male-s5NqE0rZ/founder/3.mp3 +0 -0
  291. package/dist/remotion-bundle/public/paper-slide-experiments/career-advice/0.mp3 +0 -0
  292. package/dist/remotion-bundle/public/paper-slide-experiments/career-advice/1.mp3 +0 -0
  293. package/dist/remotion-bundle/public/paper-slide-experiments/career-advice/2.mp3 +0 -0
  294. package/dist/remotion-bundle/public/paper-slide-experiments/career-advice/3.mp3 +0 -0
  295. package/dist/remotion-bundle/public/paper-slide-experiments/career-advice/4.mp3 +0 -0
  296. package/dist/remotion-bundle/public/paper-slide-experiments/founder-lesson/0.mp3 +0 -0
  297. package/dist/remotion-bundle/public/paper-slide-experiments/founder-lesson/1.mp3 +0 -0
  298. package/dist/remotion-bundle/public/paper-slide-experiments/founder-lesson/2.mp3 +0 -0
  299. package/dist/remotion-bundle/public/paper-slide-experiments/founder-lesson/3.mp3 +0 -0
  300. package/dist/remotion-bundle/public/paper-slide-experiments/founder-lesson/4.mp3 +0 -0
  301. package/dist/remotion-bundle/public/paper-slide-experiments/incident-review/0.mp3 +0 -0
  302. package/dist/remotion-bundle/public/paper-slide-experiments/incident-review/1.mp3 +0 -0
  303. package/dist/remotion-bundle/public/paper-slide-experiments/incident-review/2.mp3 +0 -0
  304. package/dist/remotion-bundle/public/paper-slide-experiments/incident-review/3.mp3 +0 -0
  305. package/dist/remotion-bundle/public/paper-slide-experiments/incident-review/4.mp3 +0 -0
  306. package/dist/remotion-bundle/public/paper-slide-experiments/learning-loop/0.mp3 +0 -0
  307. package/dist/remotion-bundle/public/paper-slide-experiments/learning-loop/1.mp3 +0 -0
  308. package/dist/remotion-bundle/public/paper-slide-experiments/learning-loop/2.mp3 +0 -0
  309. package/dist/remotion-bundle/public/paper-slide-experiments/learning-loop/3.mp3 +0 -0
  310. package/dist/remotion-bundle/public/paper-slide-experiments/learning-loop/4.mp3 +0 -0
  311. package/dist/remotion-bundle/public/paper-slide-experiments/meeting-closure/0.mp3 +0 -0
  312. package/dist/remotion-bundle/public/paper-slide-experiments/meeting-closure/1.mp3 +0 -0
  313. package/dist/remotion-bundle/public/paper-slide-experiments/meeting-closure/2.mp3 +0 -0
  314. package/dist/remotion-bundle/public/paper-slide-experiments/meeting-closure/3.mp3 +0 -0
  315. package/dist/remotion-bundle/public/paper-slide-experiments/product-update/0.mp3 +0 -0
  316. package/dist/remotion-bundle/public/paper-slide-experiments/product-update/1.mp3 +0 -0
  317. package/dist/remotion-bundle/public/paper-slide-experiments/product-update/2.mp3 +0 -0
  318. package/dist/remotion-bundle/public/paper-slide-experiments/product-update/3.mp3 +0 -0
  319. package/dist/remotion-bundle/public/paper-slide-experiments/research-reading/0.mp3 +0 -0
  320. package/dist/remotion-bundle/public/paper-slide-experiments/research-reading/1.mp3 +0 -0
  321. package/dist/remotion-bundle/public/paper-slide-experiments/research-reading/2.mp3 +0 -0
  322. package/dist/remotion-bundle/public/paper-slide-experiments/research-reading/3.mp3 +0 -0
  323. package/dist/remotion-bundle/public/paper-slide-experiments/sales-enablement/0.mp3 +0 -0
  324. package/dist/remotion-bundle/public/paper-slide-experiments/sales-enablement/1.mp3 +0 -0
  325. package/dist/remotion-bundle/public/paper-slide-experiments/sales-enablement/2.mp3 +0 -0
  326. package/dist/remotion-bundle/public/paper-slide-experiments/sales-enablement/3.mp3 +0 -0
  327. package/dist/remotion-bundle/public/paper-slide-experiments/sales-enablement/4.mp3 +0 -0
  328. package/dist/remotion-bundle/public/voiceover/ai-life/card-0.mp3 +0 -0
  329. package/dist/remotion-bundle/public/voiceover/ai-life/card-1.mp3 +0 -0
  330. package/dist/remotion-bundle/public/voiceover/ai-life/card-2.mp3 +0 -0
  331. package/dist/remotion-bundle/public/voiceover/ai-life/card-3.mp3 +0 -0
  332. package/dist/remotion-bundle/public/voiceover/ai-life/card-4.mp3 +0 -0
  333. package/dist/remotion-bundle/public/voiceover/ai-life/card-5.mp3 +0 -0
  334. package/dist/remotion-bundle/public/voiceover/coffee-science/card-0.mp3 +0 -0
  335. package/dist/remotion-bundle/public/voiceover/coffee-science/card-1.mp3 +0 -0
  336. package/dist/remotion-bundle/public/voiceover/coffee-science/card-2.mp3 +0 -0
  337. package/dist/remotion-bundle/public/voiceover/coffee-science/card-3.mp3 +0 -0
  338. package/dist/remotion-bundle/public/voiceover/coffee-science/card-4.mp3 +0 -0
  339. package/dist/remotion-bundle/public/voiceover/coffee-science/card-5.mp3 +0 -0
  340. package/dist/remotion-bundle/public/voiceover/coffee-science/card-6.mp3 +0 -0
  341. package/dist/remotion-bundle/public/voiceover/reading-secrets/card-0.mp3 +0 -0
  342. package/dist/remotion-bundle/public/voiceover/reading-secrets/card-1.mp3 +0 -0
  343. package/dist/remotion-bundle/public/voiceover/reading-secrets/card-2.mp3 +0 -0
  344. package/dist/remotion-bundle/public/voiceover/reading-secrets/card-3.mp3 +0 -0
  345. package/dist/remotion-bundle/public/voiceover/reading-secrets/card-4.mp3 +0 -0
  346. package/dist/remotion-bundle/public/voiceover/reading-secrets/card-5.mp3 +0 -0
  347. package/dist/remotion-bundle/public/voiceover/reading-secrets/card-6.mp3 +0 -0
  348. package/dist/remotion-bundle/public/voiceover/remote-work/card-0.mp3 +0 -0
  349. package/dist/remotion-bundle/public/voiceover/remote-work/card-1.mp3 +0 -0
  350. package/dist/remotion-bundle/public/voiceover/remote-work/card-2.mp3 +0 -0
  351. package/dist/remotion-bundle/public/voiceover/remote-work/card-3.mp3 +0 -0
  352. package/dist/remotion-bundle/public/voiceover/remote-work/card-4.mp3 +0 -0
  353. package/dist/remotion-bundle/public/voiceover/remote-work/card-5.mp3 +0 -0
  354. package/dist/remotion-bundle/source-map-helper.wasm +0 -0
  355. package/lib/cli.js +270 -0
  356. package/lib/commands/_registry.js +48 -0
  357. package/lib/commands/add.js +242 -0
  358. package/lib/commands/asr/azure-transcribe.js +336 -0
  359. package/lib/commands/asr/cloud-transcribe.js +384 -0
  360. package/lib/commands/asr/helpers.js +76 -0
  361. package/lib/commands/asr/index.js +236 -0
  362. package/lib/commands/asr/local-transcribe.js +125 -0
  363. package/lib/commands/asr-jobs.js +257 -0
  364. package/lib/commands/asr.js +11 -0
  365. package/lib/commands/auth-cmds.js +358 -0
  366. package/lib/commands/dub.js +542 -0
  367. package/lib/commands/explain.js +512 -0
  368. package/lib/commands/feedback.js +152 -0
  369. package/lib/commands/image.js +207 -0
  370. package/lib/commands/mcp-key.js +166 -0
  371. package/lib/commands/narrate.js +639 -0
  372. package/lib/commands/picstory-templates.js +276 -0
  373. package/lib/commands/picstory.js +547 -0
  374. package/lib/commands/podcast/dialogue.js +109 -0
  375. package/lib/commands/podcast/generate.js +127 -0
  376. package/lib/commands/podcast/index.js +561 -0
  377. package/lib/commands/podcast/synthesize.js +188 -0
  378. package/lib/commands/podcast.js +11 -0
  379. package/lib/commands/present.js +519 -0
  380. package/lib/commands/publish.js +415 -0
  381. package/lib/commands/skills.js +473 -0
  382. package/lib/commands/slice-render.js +282 -0
  383. package/lib/commands/slice-stage.js +264 -0
  384. package/lib/commands/slice.js +346 -0
  385. package/lib/commands/slides/constants.js +108 -0
  386. package/lib/commands/slides/html-renderer.js +338 -0
  387. package/lib/commands/slides/index.js +345 -0
  388. package/lib/commands/slides.js +11 -0
  389. package/lib/commands/story.js +302 -0
  390. package/lib/commands/summarize.js +532 -0
  391. package/lib/commands/synthesize.js +261 -0
  392. package/lib/commands/translate.js +593 -0
  393. package/lib/commands/upgrade.js +249 -0
  394. package/lib/commands/video-translate.js +577 -0
  395. package/lib/commands/voices.js +292 -0
  396. package/lib/core/agent-env.js +104 -0
  397. package/lib/core/args.js +107 -0
  398. package/lib/core/asr-client.js +448 -0
  399. package/lib/core/asr-jobs-client.js +126 -0
  400. package/lib/core/asr-jobs-store.js +105 -0
  401. package/lib/core/asr-r2-upload.js +181 -0
  402. package/lib/core/asr-upload.js +132 -0
  403. package/lib/core/audio-extract.js +150 -0
  404. package/lib/core/audio.js +219 -0
  405. package/lib/core/auth.js +880 -0
  406. package/lib/core/config.js +197 -0
  407. package/lib/core/feedback.js +64 -0
  408. package/lib/core/ffmpeg.js +476 -0
  409. package/lib/core/http.js +188 -0
  410. package/lib/core/image-client.js +55 -0
  411. package/lib/core/intent-params.js +11 -0
  412. package/lib/core/llm-client.js +76 -0
  413. package/lib/core/logger.js +208 -0
  414. package/lib/core/mic-recorder.js +182 -0
  415. package/lib/core/pause-markers.js +94 -0
  416. package/lib/core/podcast-pacing.js +118 -0
  417. package/lib/core/spinner.js +33 -0
  418. package/lib/core/srt.js +394 -0
  419. package/lib/core/telemetry.js +100 -0
  420. package/lib/core/timeline.js +92 -0
  421. package/lib/core/tts-synthesizer.js +70 -0
  422. package/lib/core/update-check.js +185 -0
  423. package/lib/core/url-download.js +148 -0
  424. package/lib/core/whisper-local.js +279 -0
  425. package/lib/internal/deck-validator.js +488 -0
  426. package/lib/internal/slice-themes.json +370 -0
  427. package/lib/stage-core/cloud-render.js +170 -0
  428. package/lib/stage-core/deck-format.js +133 -0
  429. package/lib/stage-core/edit-prompt.js +104 -0
  430. package/lib/stage-core/event-bus.js +31 -0
  431. package/lib/stage-core/port.js +46 -0
  432. package/lib/stage-core/server.js +352 -0
  433. package/lib/stage-core/snapshot-store.js +198 -0
  434. package/lib/stage-core/watcher.js +106 -0
  435. package/lib/stage-ui/slice/template.js +1672 -0
  436. package/package.json +9 -4
  437. package/skills/.claude-plugin/marketplace.json +22 -0
  438. package/skills/.claude-plugin/plugin.json +25 -0
  439. package/skills/LICENSE +21 -0
  440. package/skills/README.md +120 -0
  441. package/skills/hub/SKILL.md +317 -0
  442. package/skills/podcast/SKILL.md +146 -0
  443. package/skills/slice/SKILL.md +205 -0
  444. package/skills/slice/agents/openai.yaml +4 -0
  445. package/skills/slice/references/deck-schema.md +183 -0
  446. package/skills/slice/references/example-decks.md +108 -0
  447. package/skills/slice/references/themes.md +172 -0
  448. package/skills/transcribe/SKILL.md +473 -0
  449. package/skills/video/SKILL.md +261 -0
  450. package/skills/voxflow-slice/SKILL.md +271 -0
  451. package/skills/voxflow-slice/examples/article.md +13 -0
  452. package/skills/voxflow-slice/examples/expected-deck.json +39 -0
  453. package/skills/voxflow-slice/examples/validate.mjs +46 -0
@@ -0,0 +1,448 @@
1
+ /**
2
+ * VoxFlow CLI — ASR (Automatic Speech Recognition) API client
3
+ *
4
+ * Wraps the three backend ASR endpoints:
5
+ * - POST /api/asr/sentence (≤60 s, sync, base64 or URL)
6
+ * - POST /api/asr/flash (≤2 h, sync, URL only)
7
+ * - POST /api/asr/file (≤5 h, async → poll for result)
8
+ *
9
+ * Provides automatic mode selection based on audio duration.
10
+ */
11
+
12
+ const fs = require('fs');
13
+ const { request, throwApiError, throwNetworkError, ApiError } = require('./http');
14
+
15
+ // ─── Constants ──────────────────────────────────────────────────────────────
16
+
17
+ /** Upper duration limit for sentence recognition (ms) */
18
+ const SENTENCE_MAX_MS = 60_000;
19
+
20
+ /** Upper duration limit for flash recognition (ms) */
21
+ const FLASH_MAX_MS = 7_200_000;
22
+
23
+ /** Maximum file size for base64 upload (bytes). Above this, use COS URL. */
24
+ const BASE64_MAX_BYTES = 5 * 1024 * 1024;
25
+
26
+ /** Default polling interval for async file tasks (ms) */
27
+ const POLL_INTERVAL_MS = 3_000;
28
+
29
+ /** Maximum polling duration before giving up (ms) — 5 minutes */
30
+ const POLL_TIMEOUT_MS = 300_000;
31
+
32
+ // Task status codes returned by DescribeTaskStatus
33
+ const TASK_STATUS = {
34
+ WAITING: 0,
35
+ PROCESSING: 1,
36
+ SUCCESS: 2,
37
+ FAILED: 3,
38
+ };
39
+
40
+ // ─── Mode detection ─────────────────────────────────────────────────────────
41
+
42
+ /**
43
+ * Choose the best ASR mode based on audio duration and input type.
44
+ *
45
+ * @param {number} durationMs - Audio duration in milliseconds
46
+ * @param {boolean} hasUrl - Whether a URL is available (COS or remote)
47
+ * @param {number} fileSize - File size in bytes
48
+ * @returns {'sentence'|'flash'|'file'}
49
+ */
50
+ function detectMode(durationMs, hasUrl, fileSize) {
51
+ if (durationMs <= SENTENCE_MAX_MS && fileSize <= BASE64_MAX_BYTES) {
52
+ return 'sentence';
53
+ }
54
+ if (durationMs <= FLASH_MAX_MS && hasUrl) {
55
+ return 'flash';
56
+ }
57
+ return 'file';
58
+ }
59
+
60
+ // ─── Request helpers ────────────────────────────────────────────────────────
61
+
62
+ function authHeaders(token) {
63
+ return {
64
+ 'Content-Type': 'application/json',
65
+ Authorization: `Bearer ${token}`,
66
+ };
67
+ }
68
+
69
+ // ─── Sentence recognition ───────────────────────────────────────────────────
70
+
71
+ /**
72
+ * Recognize audio ≤ 60 s using SentenceRecognition (sync).
73
+ *
74
+ * @param {object} opts
75
+ * @param {string} opts.apiBase
76
+ * @param {string} opts.token
77
+ * @param {string} [opts.url] - COS / remote URL
78
+ * @param {string} [opts.filePath] - Local file (will be base64-encoded)
79
+ * @param {string} [opts.lang] - Engine model, e.g. '16k_zh' (default)
80
+ * @param {boolean}[opts.wordInfo] - Return word-level timestamps
81
+ * @returns {Promise<{result: string, audioTime: number, wordList?: Array, requestId: string, quota: object}>}
82
+ */
83
+ async function recognizeSentence(opts) {
84
+ const { apiBase, token, url, filePath, lang = '16k_zh', wordInfo = false } = opts;
85
+
86
+ const body = {
87
+ EngSerViceType: lang, // Note: backend uses this exact (mis)spelling
88
+ VoiceFormat: 'wav',
89
+ SubServiceType: 2,
90
+ WordInfo: wordInfo ? 1 : 0,
91
+ ConvertNumMode: 1,
92
+ };
93
+
94
+ if (url) {
95
+ body.Url = url;
96
+ body.SourceType = 0;
97
+ } else if (filePath) {
98
+ const buf = fs.readFileSync(filePath);
99
+ body.Data = buf.toString('base64');
100
+ body.DataLen = buf.length;
101
+ body.SourceType = 1;
102
+ } else {
103
+ throw new Error('Either url or filePath is required for sentence recognition');
104
+ }
105
+
106
+ try {
107
+ const { status, data } = await request(
108
+ `${apiBase}/api/asr/sentence`,
109
+ { method: 'POST', headers: authHeaders(token) },
110
+ body
111
+ );
112
+
113
+ if (status !== 200 || data.code !== 'success') {
114
+ throwApiError(status, data, 'ASR sentence');
115
+ }
116
+
117
+ return {
118
+ result: data.result,
119
+ audioTime: data.audioTime,
120
+ wordList: data.wordList || [],
121
+ requestId: data.requestId,
122
+ quota: data.quota,
123
+ };
124
+ } catch (err) {
125
+ if (err instanceof ApiError) throw err;
126
+ throwNetworkError(err, apiBase);
127
+ }
128
+ }
129
+
130
+ // ─── Flash recognition ──────────────────────────────────────────────────────
131
+
132
+ /**
133
+ * Recognize audio ≤ 2 h using Flash API (sync, URL only).
134
+ *
135
+ * @param {object} opts
136
+ * @param {string} opts.apiBase
137
+ * @param {string} opts.token
138
+ * @param {string} opts.url - Audio URL (required)
139
+ * @param {string} [opts.lang] - Engine type, e.g. '16k_zh'
140
+ * @param {boolean}[opts.speakerDiarization] - Enable speaker separation
141
+ * @param {number} [opts.speakerNumber] - Expected speaker count
142
+ * @returns {Promise<{flashResult: Array, audioDuration: number, requestId: string, quota: object}>}
143
+ */
144
+ async function recognizeFlash(opts) {
145
+ const {
146
+ apiBase, token, url, lang = '16k_zh',
147
+ speakerDiarization = false, speakerNumber = 0,
148
+ } = opts;
149
+
150
+ if (!url) {
151
+ throw new Error('Flash recognition requires a URL (cannot use base64 data)');
152
+ }
153
+
154
+ const body = {
155
+ engine_type: lang,
156
+ voice_format: 'wav',
157
+ url,
158
+ speaker_diarization: speakerDiarization ? 1 : 0,
159
+ speaker_number: speakerNumber,
160
+ filter_dirty: 0,
161
+ filter_modal: 0,
162
+ filter_punc: 0,
163
+ convert_num_mode: 1,
164
+ word_info: 1,
165
+ first_channel_only: 1,
166
+ };
167
+
168
+ try {
169
+ const { status, data } = await request(
170
+ `${apiBase}/api/asr/flash`,
171
+ { method: 'POST', headers: authHeaders(token) },
172
+ body
173
+ );
174
+
175
+ if (status !== 200 || data.code !== 'success') {
176
+ throwApiError(status, data, 'ASR flash');
177
+ }
178
+
179
+ return {
180
+ flashResult: data.flash_result || [],
181
+ audioDuration: data.audio_duration || 0,
182
+ requestId: data.request_id,
183
+ quota: data.quota,
184
+ };
185
+ } catch (err) {
186
+ if (err instanceof ApiError) throw err;
187
+ throwNetworkError(err, apiBase);
188
+ }
189
+ }
190
+
191
+ // ─── File recognition (async) ───────────────────────────────────────────────
192
+
193
+ /**
194
+ * Submit a long audio file for async recognition (CreateRecTask).
195
+ *
196
+ * @param {object} opts
197
+ * @param {string} opts.apiBase
198
+ * @param {string} opts.token
199
+ * @param {string} [opts.url]
200
+ * @param {string} [opts.filePath] - Local file (base64, ≤5 MB only)
201
+ * @param {string} [opts.lang] - e.g. '16k_zh'
202
+ * @param {boolean}[opts.speakerDiarization]
203
+ * @param {number} [opts.speakerNumber]
204
+ * @returns {Promise<{taskId: number, requestId: string, quota: object}>}
205
+ */
206
+ async function submitFileTask(opts) {
207
+ const {
208
+ apiBase, token, url, filePath, lang = '16k_zh',
209
+ speakerDiarization = false, speakerNumber = 0,
210
+ } = opts;
211
+
212
+ const body = {
213
+ EngineModelType: lang,
214
+ ChannelNum: 1,
215
+ ResTextFormat: 0,
216
+ FilterDirty: 0,
217
+ FilterModal: 0,
218
+ FilterPunc: 0,
219
+ ConvertNumMode: 1,
220
+ SpeakerDiarization: speakerDiarization ? 1 : 0,
221
+ SpeakerNumber: speakerNumber,
222
+ };
223
+
224
+ if (url) {
225
+ body.Url = url;
226
+ body.SourceType = 0;
227
+ } else if (filePath) {
228
+ const buf = fs.readFileSync(filePath);
229
+ if (buf.length > BASE64_MAX_BYTES) {
230
+ throw new Error(
231
+ `File too large for base64 upload (${(buf.length / 1024 / 1024).toFixed(1)} MB). ` +
232
+ 'Upload to COS first or use flash mode with a URL.'
233
+ );
234
+ }
235
+ body.Data = buf.toString('base64');
236
+ body.DataLen = buf.length;
237
+ body.SourceType = 1;
238
+ } else {
239
+ throw new Error('Either url or filePath is required for file recognition');
240
+ }
241
+
242
+ try {
243
+ const { status, data } = await request(
244
+ `${apiBase}/api/asr/file`,
245
+ { method: 'POST', headers: authHeaders(token) },
246
+ body
247
+ );
248
+
249
+ if (status !== 200 || data.code !== 'success') {
250
+ throwApiError(status, data, 'ASR file submit');
251
+ }
252
+
253
+ return {
254
+ taskId: data.taskId,
255
+ requestId: data.requestId,
256
+ quota: data.quota,
257
+ };
258
+ } catch (err) {
259
+ if (err instanceof ApiError) throw err;
260
+ throwNetworkError(err, apiBase);
261
+ }
262
+ }
263
+
264
+ /**
265
+ * Poll a file recognition task until it completes or fails.
266
+ *
267
+ * @param {object} opts
268
+ * @param {string} opts.apiBase
269
+ * @param {string} opts.token
270
+ * @param {number} opts.taskId
271
+ * @param {number} [opts.pollIntervalMs] - ms between polls (default 3000)
272
+ * @param {number} [opts.pollTimeoutMs] - max wait (default 300000)
273
+ * @param {function} [opts.onProgress] - callback(status, elapsed) for UI updates
274
+ * @returns {Promise<{result: string, audioTime: number, status: number}>}
275
+ */
276
+ async function pollTaskResult(opts) {
277
+ const {
278
+ apiBase, token, taskId,
279
+ pollIntervalMs = POLL_INTERVAL_MS,
280
+ pollTimeoutMs = POLL_TIMEOUT_MS,
281
+ onProgress,
282
+ } = opts;
283
+
284
+ const started = Date.now();
285
+
286
+ // eslint-disable-next-line no-constant-condition -- task-poll loop, exits via throw/return
287
+ while (true) {
288
+ const elapsed = Date.now() - started;
289
+ if (elapsed > pollTimeoutMs) {
290
+ throw new Error(
291
+ `ASR task ${taskId} timed out after ${Math.round(elapsed / 1000)}s. ` +
292
+ 'The task may still complete — check later with: voxflow asr --task-id ' + taskId
293
+ );
294
+ }
295
+
296
+ try {
297
+ const { status, data } = await request(
298
+ `${apiBase}/api/asr/result/${taskId}`,
299
+ { method: 'GET', headers: authHeaders(token) }
300
+ );
301
+
302
+ if (status !== 200 || data.code !== 'success') {
303
+ throwApiError(status, data, 'ASR poll');
304
+ }
305
+
306
+ const taskData = data.data;
307
+ const taskStatus = taskData.Status;
308
+
309
+ if (onProgress) onProgress(taskStatus, elapsed);
310
+
311
+ if (taskStatus === TASK_STATUS.SUCCESS) {
312
+ return {
313
+ result: taskData.Result,
314
+ audioTime: taskData.AudioTime,
315
+ status: taskStatus,
316
+ };
317
+ }
318
+
319
+ if (taskStatus === TASK_STATUS.FAILED) {
320
+ throw new Error(
321
+ `ASR task ${taskId} failed: ${taskData.Result || 'Unknown error'}`
322
+ );
323
+ }
324
+
325
+ // Still waiting or processing — sleep then retry
326
+ } catch (err) {
327
+ if (err instanceof ApiError) throw err;
328
+ // Transient network errors during polling — log and retry
329
+ if (elapsed + pollIntervalMs < pollTimeoutMs) {
330
+ // swallow and retry
331
+ } else {
332
+ throwNetworkError(err, apiBase);
333
+ }
334
+ }
335
+
336
+ await sleep(pollIntervalMs);
337
+ }
338
+ }
339
+
340
+ // ─── Unified recognize function ─────────────────────────────────────────────
341
+
342
+ /**
343
+ * High-level: recognize audio using the best available mode.
344
+ *
345
+ * @param {object} opts
346
+ * @param {string} opts.apiBase
347
+ * @param {string} opts.token
348
+ * @param {string} opts.mode - 'auto', 'sentence', 'flash', 'file'
349
+ * @param {string} [opts.url] - Remote or COS URL
350
+ * @param {string} [opts.filePath] - Local WAV file path
351
+ * @param {number} opts.durationMs - Audio duration in ms
352
+ * @param {number} [opts.fileSize] - File size in bytes
353
+ * @param {string} [opts.lang] - Engine model (default '16k_zh')
354
+ * @param {boolean} [opts.speakerDiarization]
355
+ * @param {number} [opts.speakerNumber]
356
+ * @param {boolean} [opts.wordInfo]
357
+ * @param {function}[opts.onProgress] - Polling progress callback
358
+ * @returns {Promise<{mode: string, result: string, flashResult?: Array, audioTime: number, wordList?: Array, quota: object}>}
359
+ */
360
+ async function recognize(opts) {
361
+ const {
362
+ mode: requestedMode = 'auto',
363
+ url, durationMs, fileSize = 0,
364
+ } = opts;
365
+
366
+ // Determine actual mode
367
+ const hasUrl = !!url;
368
+ const actualMode = requestedMode === 'auto'
369
+ ? detectMode(durationMs, hasUrl, fileSize)
370
+ : requestedMode;
371
+
372
+ switch (actualMode) {
373
+ case 'sentence': {
374
+ const res = await recognizeSentence(opts);
375
+ return {
376
+ mode: 'sentence',
377
+ result: res.result,
378
+ audioTime: res.audioTime,
379
+ wordList: res.wordList,
380
+ quota: res.quota,
381
+ };
382
+ }
383
+
384
+ case 'flash': {
385
+ if (!url) {
386
+ throw new Error(
387
+ 'Flash mode requires a URL. Upload the file to COS first, or use --mode auto.'
388
+ );
389
+ }
390
+ const res = await recognizeFlash(opts);
391
+ // Flatten flash segments into a single result text
392
+ const fullText = (res.flashResult || [])
393
+ .flatMap((seg) =>
394
+ seg.sentence_list
395
+ ? seg.sentence_list.map((s) => s.text)
396
+ : [seg.text]
397
+ )
398
+ .join('');
399
+ return {
400
+ mode: 'flash',
401
+ result: fullText,
402
+ flashResult: res.flashResult,
403
+ audioDuration: res.audioDuration,
404
+ audioTime: (res.audioDuration || 0) / 1000,
405
+ quota: res.quota,
406
+ };
407
+ }
408
+
409
+ case 'file': {
410
+ const submitRes = await submitFileTask(opts);
411
+ const pollRes = await pollTaskResult({
412
+ apiBase: opts.apiBase,
413
+ token: opts.token,
414
+ taskId: submitRes.taskId,
415
+ onProgress: opts.onProgress,
416
+ });
417
+ return {
418
+ mode: 'file',
419
+ result: pollRes.result,
420
+ audioTime: pollRes.audioTime,
421
+ taskId: submitRes.taskId,
422
+ quota: submitRes.quota,
423
+ };
424
+ }
425
+
426
+ default:
427
+ throw new Error(`Unknown ASR mode: ${actualMode}. Use: auto, sentence, flash, or file`);
428
+ }
429
+ }
430
+
431
+ // ─── Utility ────────────────────────────────────────────────────────────────
432
+
433
+ function sleep(ms) {
434
+ return new Promise((resolve) => setTimeout(resolve, ms));
435
+ }
436
+
437
+ module.exports = {
438
+ recognize,
439
+ recognizeSentence,
440
+ recognizeFlash,
441
+ submitFileTask,
442
+ pollTaskResult,
443
+ detectMode,
444
+ SENTENCE_MAX_MS,
445
+ FLASH_MAX_MS,
446
+ BASE64_MAX_BYTES,
447
+ TASK_STATUS,
448
+ };
@@ -0,0 +1,126 @@
1
+ /**
2
+ * VoxFlow CLI — HTTP client for /api/asr/jobs/*.
3
+ *
4
+ * Thin wrappers over `request()` — they unwrap the standard
5
+ * `{ code: 'success', data: { ... } }` envelope and surface non-2xx as
6
+ * ApiError so callers can match on `err.code`/`err.status`.
7
+ *
8
+ * The backend lazy-polls Azure on every GET /api/asr/jobs/:id, so the CLI
9
+ * just calls getJob in a backoff loop without needing a separate "sync" RPC.
10
+ */
11
+
12
+ const { request, throwApiError, throwNetworkError } = require('./http');
13
+
14
+ function authHeaders(token) {
15
+ return {
16
+ 'Content-Type': 'application/json',
17
+ Authorization: `Bearer ${token}`
18
+ };
19
+ }
20
+
21
+ async function call(apiBase, method, path, token, body) {
22
+ let res;
23
+ try {
24
+ res = await request(`${apiBase}${path}`, {
25
+ method,
26
+ headers: authHeaders(token)
27
+ }, body);
28
+ } catch (err) {
29
+ throwNetworkError(err, apiBase);
30
+ }
31
+ if (res.status < 200 || res.status >= 300) {
32
+ throwApiError(res.status, res.data, `ASR jobs ${method} ${path}`);
33
+ }
34
+ return res.data?.data || res.data;
35
+ }
36
+
37
+ /**
38
+ * Create a new ASR job. Charges quota and submits to Azure server-side.
39
+ *
40
+ * @param {object} args
41
+ * @param {string} args.apiBase
42
+ * @param {string} args.token
43
+ * @param {string} args.sourceKey R2 key returned by uploadToR2()
44
+ * @param {string} [args.sourceFilename]
45
+ * @param {number} [args.sourceSize]
46
+ * @param {number} args.durationSec used to compute quota cost server-side
47
+ * @param {string} [args.language='auto']
48
+ * @param {object} [args.options] { wordTimestamps?, diarize?, speakerCount?, candidateLocales? }
49
+ */
50
+ async function createJob({
51
+ apiBase, token, sourceKey, sourceFilename, sourceSize,
52
+ durationSec, language = 'auto', options = {}
53
+ }) {
54
+ const data = await call(apiBase, 'POST', '/api/asr/jobs', token, {
55
+ sourceKey,
56
+ sourceFilename,
57
+ sourceSize,
58
+ durationSec,
59
+ language,
60
+ options
61
+ });
62
+ return data.job;
63
+ }
64
+
65
+ async function getJob(apiBase, token, jobId) {
66
+ const data = await call(apiBase, 'GET', `/api/asr/jobs/${encodeURIComponent(jobId)}`, token);
67
+ return data.job;
68
+ }
69
+
70
+ async function listJobs(apiBase, token, { limit = 20, before } = {}) {
71
+ const params = new URLSearchParams();
72
+ if (limit) params.set('limit', String(limit));
73
+ if (before) params.set('before', before);
74
+ const qs = params.toString();
75
+ const data = await call(apiBase, 'GET', `/api/asr/jobs${qs ? `?${qs}` : ''}`, token);
76
+ return data.jobs || [];
77
+ }
78
+
79
+ async function cancelJob(apiBase, token, jobId) {
80
+ const data = await call(apiBase, 'DELETE', `/api/asr/jobs/${encodeURIComponent(jobId)}`, token);
81
+ return data.job;
82
+ }
83
+
84
+ const TERMINAL_STATUSES = new Set(['succeeded', 'failed', 'cancelled']);
85
+
86
+ /**
87
+ * Poll the backend until the job hits a terminal state (or `signal` aborts).
88
+ *
89
+ * Backoff schedule keeps the prompt cache warm for short jobs and slows down
90
+ * for long ones: 2s → 5s → 10s → 30s capped.
91
+ *
92
+ * @param {object} args
93
+ * @param {string} args.apiBase
94
+ * @param {string} args.token
95
+ * @param {string} args.jobId
96
+ * @param {function} [args.onProgress] (job) => void called every poll
97
+ * @param {AbortSignal} [args.signal]
98
+ * @returns {Promise<object>} terminal job row
99
+ */
100
+ async function waitForTerminal({ apiBase, token, jobId, onProgress, signal }) {
101
+ const intervals = [2000, 5000, 10000, 30000];
102
+ let i = 0;
103
+ while (true) {
104
+ if (signal?.aborted) {
105
+ const e = new Error('Aborted');
106
+ e.code = 'aborted';
107
+ throw e;
108
+ }
109
+ const job = await getJob(apiBase, token, jobId);
110
+ if (onProgress) onProgress(job);
111
+ if (TERMINAL_STATUSES.has(job.status)) return job;
112
+ const delay = intervals[Math.min(i, intervals.length - 1)];
113
+ await new Promise((resolve) => setTimeout(resolve, delay));
114
+ i++;
115
+ }
116
+ }
117
+
118
+ module.exports = {
119
+ createJob,
120
+ getJob,
121
+ listJobs,
122
+ cancelJob,
123
+ waitForTerminal,
124
+ TERMINAL_STATUSES,
125
+ __internals: { call }
126
+ };
@@ -0,0 +1,105 @@
1
+ /**
2
+ * VoxFlow CLI — Local persistence for in-flight ASR jobs.
3
+ *
4
+ * Why: ASR jobs run minutes-to-hours server-side. If the user closes the
5
+ * terminal or hits Ctrl+C, the server-side job keeps going (Azure doesn't
6
+ * care that we walked away). This store keeps a tiny pointer file per job
7
+ * so the next `voxflow asr` invocation can find and resume them.
8
+ *
9
+ * Layout:
10
+ * ~/.config/voxflow/jobs/asr-<jobId>.json
11
+ *
12
+ * Schema (kept minimal — backend is the source of truth):
13
+ * {
14
+ * jobId, createdAt, status, source: { path, filename, durationSec },
15
+ * language, options, lastPolledAt
16
+ * }
17
+ *
18
+ * The file is rewritten on every poll so a stale `status` field always
19
+ * matches the last network observation. Cancel/cleanup deletes the file.
20
+ */
21
+
22
+ const fs = require('fs');
23
+ const path = require('path');
24
+ const { getConfigDir } = require('./config');
25
+
26
+ function getJobsDir() {
27
+ const dir = path.join(getConfigDir(), 'jobs');
28
+ fs.mkdirSync(dir, { recursive: true });
29
+ return dir;
30
+ }
31
+
32
+ function jobFilePath(jobId) {
33
+ // jobId is a UUID from the backend — safe to interpolate, but defend
34
+ // against future format changes by stripping anything path-unsafe.
35
+ const safe = String(jobId).replace(/[^A-Za-z0-9._-]/g, '_');
36
+ return path.join(getJobsDir(), `asr-${safe}.json`);
37
+ }
38
+
39
+ function saveJob(jobId, record) {
40
+ const file = jobFilePath(jobId);
41
+ const payload = JSON.stringify({
42
+ jobId,
43
+ ...record,
44
+ lastPolledAt: new Date().toISOString()
45
+ }, null, 2);
46
+ // atomic write: tmp + rename so a Ctrl+C mid-write doesn't corrupt.
47
+ const tmp = `${file}.tmp`;
48
+ fs.writeFileSync(tmp, payload);
49
+ fs.renameSync(tmp, file);
50
+ return file;
51
+ }
52
+
53
+ function loadJob(jobId) {
54
+ const file = jobFilePath(jobId);
55
+ if (!fs.existsSync(file)) return null;
56
+ try {
57
+ return JSON.parse(fs.readFileSync(file, 'utf8'));
58
+ } catch {
59
+ return null;
60
+ }
61
+ }
62
+
63
+ function listJobs() {
64
+ const dir = getJobsDir();
65
+ const files = fs.readdirSync(dir).filter((f) => f.startsWith('asr-') && f.endsWith('.json'));
66
+ return files
67
+ .map((f) => {
68
+ try { return JSON.parse(fs.readFileSync(path.join(dir, f), 'utf8')); }
69
+ catch { return null; }
70
+ })
71
+ .filter(Boolean)
72
+ .sort((a, b) => (b.createdAt || '').localeCompare(a.createdAt || ''));
73
+ }
74
+
75
+ function deleteJob(jobId) {
76
+ const file = jobFilePath(jobId);
77
+ try { fs.unlinkSync(file); return true; } catch { return false; }
78
+ }
79
+
80
+ // Prune jobs that have been in a terminal state for more than `maxAgeDays`.
81
+ // Called opportunistically by the asr command so the dir doesn't grow forever.
82
+ function pruneTerminal(maxAgeDays = 30) {
83
+ const cutoff = Date.now() - maxAgeDays * 24 * 60 * 60 * 1000;
84
+ let removed = 0;
85
+ for (const job of listJobs()) {
86
+ if (!['succeeded', 'failed', 'cancelled'].includes(job.status)) continue;
87
+ const ts = Date.parse(job.lastPolledAt || job.createdAt || 0);
88
+ if (Number.isFinite(ts) && ts < cutoff) {
89
+ deleteJob(job.jobId);
90
+ removed++;
91
+ }
92
+ }
93
+ return removed;
94
+ }
95
+
96
+ module.exports = {
97
+ saveJob,
98
+ loadJob,
99
+ listJobs,
100
+ deleteJob,
101
+ pruneTerminal,
102
+ getJobsDir,
103
+ // Test helpers
104
+ __internals: { jobFilePath }
105
+ };