@kenkaiiii/gg-editor 0.6.7 → 0.7.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (455) hide show
  1. package/dist/cli.js +64 -1
  2. package/dist/cli.js.map +1 -1
  3. package/dist/core/audio-mix.d.ts.map +1 -1
  4. package/dist/core/audio-mix.js +8 -1
  5. package/dist/core/audio-mix.js.map +1 -1
  6. package/dist/core/audio-mix.test.js +1 -1
  7. package/dist/core/audio-mix.test.js.map +1 -1
  8. package/dist/core/auth/api-keys.d.ts +1 -1
  9. package/dist/core/auth/api-keys.d.ts.map +1 -1
  10. package/dist/core/auth/api-keys.js +2 -1
  11. package/dist/core/auth/api-keys.js.map +1 -1
  12. package/dist/core/auth/index.d.ts +18 -5
  13. package/dist/core/auth/index.d.ts.map +1 -1
  14. package/dist/core/auth/index.js +16 -4
  15. package/dist/core/auth/index.js.map +1 -1
  16. package/dist/core/auth/login.d.ts.map +1 -1
  17. package/dist/core/auth/login.js +4 -31
  18. package/dist/core/auth/login.js.map +1 -1
  19. package/dist/core/beats.d.ts +59 -0
  20. package/dist/core/beats.d.ts.map +1 -0
  21. package/dist/core/beats.js +122 -0
  22. package/dist/core/beats.js.map +1 -0
  23. package/dist/core/beats.test.d.ts +2 -0
  24. package/dist/core/beats.test.d.ts.map +1 -0
  25. package/dist/core/beats.test.js +86 -0
  26. package/dist/core/beats.test.js.map +1 -0
  27. package/dist/core/brand-kit.d.ts +80 -0
  28. package/dist/core/brand-kit.d.ts.map +1 -0
  29. package/dist/core/brand-kit.js +96 -0
  30. package/dist/core/brand-kit.js.map +1 -0
  31. package/dist/core/brand-kit.test.d.ts +2 -0
  32. package/dist/core/brand-kit.test.d.ts.map +1 -0
  33. package/dist/core/brand-kit.test.js +76 -0
  34. package/dist/core/brand-kit.test.js.map +1 -0
  35. package/dist/core/bundled-sfx.d.ts +64 -0
  36. package/dist/core/bundled-sfx.d.ts.map +1 -0
  37. package/dist/core/bundled-sfx.js +218 -0
  38. package/dist/core/bundled-sfx.js.map +1 -0
  39. package/dist/core/bundled-sfx.test.d.ts +2 -0
  40. package/dist/core/bundled-sfx.test.d.ts.map +1 -0
  41. package/dist/core/bundled-sfx.test.js +81 -0
  42. package/dist/core/bundled-sfx.test.js.map +1 -0
  43. package/dist/core/child-abort.d.ts +57 -0
  44. package/dist/core/child-abort.d.ts.map +1 -0
  45. package/dist/core/child-abort.js +95 -0
  46. package/dist/core/child-abort.js.map +1 -0
  47. package/dist/core/child-abort.test.d.ts +2 -0
  48. package/dist/core/child-abort.test.d.ts.map +1 -0
  49. package/dist/core/child-abort.test.js +88 -0
  50. package/dist/core/child-abort.test.js.map +1 -0
  51. package/dist/core/clip-scoring.d.ts +44 -0
  52. package/dist/core/clip-scoring.d.ts.map +1 -0
  53. package/dist/core/clip-scoring.js +165 -0
  54. package/dist/core/clip-scoring.js.map +1 -0
  55. package/dist/core/clip-scoring.test.d.ts +2 -0
  56. package/dist/core/clip-scoring.test.d.ts.map +1 -0
  57. package/dist/core/clip-scoring.test.js +113 -0
  58. package/dist/core/clip-scoring.test.js.map +1 -0
  59. package/dist/core/emoji-captions.d.ts +45 -0
  60. package/dist/core/emoji-captions.d.ts.map +1 -0
  61. package/dist/core/emoji-captions.js +121 -0
  62. package/dist/core/emoji-captions.js.map +1 -0
  63. package/dist/core/face-reframe.d.ts +91 -0
  64. package/dist/core/face-reframe.d.ts.map +1 -0
  65. package/dist/core/face-reframe.js +141 -0
  66. package/dist/core/face-reframe.js.map +1 -0
  67. package/dist/core/face-reframe.test.d.ts +2 -0
  68. package/dist/core/face-reframe.test.d.ts.map +1 -0
  69. package/dist/core/face-reframe.test.js +171 -0
  70. package/dist/core/face-reframe.test.js.map +1 -0
  71. package/dist/core/filler-words.d.ts +57 -9
  72. package/dist/core/filler-words.d.ts.map +1 -1
  73. package/dist/core/filler-words.js +61 -9
  74. package/dist/core/filler-words.js.map +1 -1
  75. package/dist/core/filler-words.test.js +91 -17
  76. package/dist/core/filler-words.test.js.map +1 -1
  77. package/dist/core/hook-rewrite.d.ts +48 -0
  78. package/dist/core/hook-rewrite.d.ts.map +1 -0
  79. package/dist/core/hook-rewrite.js +151 -0
  80. package/dist/core/hook-rewrite.js.map +1 -0
  81. package/dist/core/hook-rewrite.test.d.ts +2 -0
  82. package/dist/core/hook-rewrite.test.d.ts.map +1 -0
  83. package/dist/core/hook-rewrite.test.js +58 -0
  84. package/dist/core/hook-rewrite.test.js.map +1 -0
  85. package/dist/core/hosts/lazy.d.ts.map +1 -1
  86. package/dist/core/hosts/lazy.js +2 -0
  87. package/dist/core/hosts/lazy.js.map +1 -1
  88. package/dist/core/hosts/premiere/adapter.d.ts +1 -0
  89. package/dist/core/hosts/premiere/adapter.d.ts.map +1 -1
  90. package/dist/core/hosts/premiere/adapter.js.map +1 -1
  91. package/dist/core/hosts/premiere/bridge-source.d.ts.map +1 -1
  92. package/dist/core/hosts/premiere/bridge-source.js +6 -3
  93. package/dist/core/hosts/premiere/bridge-source.js.map +1 -1
  94. package/dist/core/hosts/resolve/adapter.d.ts +1 -0
  95. package/dist/core/hosts/resolve/adapter.d.ts.map +1 -1
  96. package/dist/core/hosts/resolve/adapter.js.map +1 -1
  97. package/dist/core/hosts/resolve/bridge-source.d.ts.map +1 -1
  98. package/dist/core/hosts/resolve/bridge-source.js +31 -4
  99. package/dist/core/hosts/resolve/bridge-source.js.map +1 -1
  100. package/dist/core/hosts/resolve/bridge.d.ts +2 -19
  101. package/dist/core/hosts/resolve/bridge.d.ts.map +1 -1
  102. package/dist/core/hosts/resolve/bridge.js +70 -41
  103. package/dist/core/hosts/resolve/bridge.js.map +1 -1
  104. package/dist/core/hosts/resolve/bridge.test.js +130 -0
  105. package/dist/core/hosts/resolve/bridge.test.js.map +1 -1
  106. package/dist/core/hosts/types.d.ts +6 -0
  107. package/dist/core/hosts/types.d.ts.map +1 -1
  108. package/dist/core/hosts/types.js.map +1 -1
  109. package/dist/core/logger.d.ts +32 -0
  110. package/dist/core/logger.d.ts.map +1 -0
  111. package/dist/core/logger.js +188 -0
  112. package/dist/core/logger.js.map +1 -0
  113. package/dist/core/loop-match.d.ts +57 -0
  114. package/dist/core/loop-match.d.ts.map +1 -0
  115. package/dist/core/loop-match.js +91 -0
  116. package/dist/core/loop-match.js.map +1 -0
  117. package/dist/core/media/ffmpeg.d.ts.map +1 -1
  118. package/dist/core/media/ffmpeg.js +14 -3
  119. package/dist/core/media/ffmpeg.js.map +1 -1
  120. package/dist/core/multi-format.d.ts +67 -0
  121. package/dist/core/multi-format.d.ts.map +1 -0
  122. package/dist/core/multi-format.js +127 -0
  123. package/dist/core/multi-format.js.map +1 -0
  124. package/dist/core/multi-format.test.d.ts +2 -0
  125. package/dist/core/multi-format.test.d.ts.map +1 -0
  126. package/dist/core/multi-format.test.js +151 -0
  127. package/dist/core/multi-format.test.js.map +1 -0
  128. package/dist/core/python/beats.py +61 -0
  129. package/dist/core/python/face_reframe.py +163 -0
  130. package/dist/core/python/sidecar-path.d.ts +13 -0
  131. package/dist/core/python/sidecar-path.d.ts.map +1 -0
  132. package/dist/core/python/sidecar-path.js +24 -0
  133. package/dist/core/python/sidecar-path.js.map +1 -0
  134. package/dist/core/python.d.ts +57 -0
  135. package/dist/core/python.d.ts.map +1 -0
  136. package/dist/core/python.js +107 -0
  137. package/dist/core/python.js.map +1 -0
  138. package/dist/core/python.test.d.ts +2 -0
  139. package/dist/core/python.test.d.ts.map +1 -0
  140. package/dist/core/python.test.js +129 -0
  141. package/dist/core/python.test.js.map +1 -0
  142. package/dist/core/retention-structure.d.ts +81 -0
  143. package/dist/core/retention-structure.d.ts.map +1 -0
  144. package/dist/core/retention-structure.js +206 -0
  145. package/dist/core/retention-structure.js.map +1 -0
  146. package/dist/core/retention-structure.test.d.ts +2 -0
  147. package/dist/core/retention-structure.test.d.ts.map +1 -0
  148. package/dist/core/retention-structure.test.js +88 -0
  149. package/dist/core/retention-structure.test.js.map +1 -0
  150. package/dist/core/review.d.ts +17 -0
  151. package/dist/core/review.d.ts.map +1 -1
  152. package/dist/core/review.js +20 -24
  153. package/dist/core/review.js.map +1 -1
  154. package/dist/core/safe-paths.d.ts +11 -0
  155. package/dist/core/safe-paths.d.ts.map +1 -1
  156. package/dist/core/safe-paths.js +26 -10
  157. package/dist/core/safe-paths.js.map +1 -1
  158. package/dist/core/safe-paths.test.js +16 -0
  159. package/dist/core/safe-paths.test.js.map +1 -1
  160. package/dist/core/skills-loader.d.ts +48 -2
  161. package/dist/core/skills-loader.d.ts.map +1 -1
  162. package/dist/core/skills-loader.js +97 -19
  163. package/dist/core/skills-loader.js.map +1 -1
  164. package/dist/core/skills-loader.test.js +63 -1
  165. package/dist/core/skills-loader.test.js.map +1 -1
  166. package/dist/core/srt.d.ts +42 -7
  167. package/dist/core/srt.d.ts.map +1 -1
  168. package/dist/core/srt.js +101 -32
  169. package/dist/core/srt.js.map +1 -1
  170. package/dist/core/srt.test.js +54 -1
  171. package/dist/core/srt.test.js.map +1 -1
  172. package/dist/core/thumbnail-compose.d.ts +58 -0
  173. package/dist/core/thumbnail-compose.d.ts.map +1 -0
  174. package/dist/core/thumbnail-compose.js +101 -0
  175. package/dist/core/thumbnail-compose.js.map +1 -0
  176. package/dist/core/thumbnail-promise.d.ts +46 -0
  177. package/dist/core/thumbnail-promise.d.ts.map +1 -0
  178. package/dist/core/thumbnail-promise.js +133 -0
  179. package/dist/core/thumbnail-promise.js.map +1 -0
  180. package/dist/core/thumbnail-promise.test.d.ts +2 -0
  181. package/dist/core/thumbnail-promise.test.d.ts.map +1 -0
  182. package/dist/core/thumbnail-promise.test.js +52 -0
  183. package/dist/core/thumbnail-promise.test.js.map +1 -0
  184. package/dist/core/viral-moments.d.ts +70 -0
  185. package/dist/core/viral-moments.d.ts.map +1 -0
  186. package/dist/core/viral-moments.js +192 -0
  187. package/dist/core/viral-moments.js.map +1 -0
  188. package/dist/core/viral-moments.test.d.ts +2 -0
  189. package/dist/core/viral-moments.test.d.ts.map +1 -0
  190. package/dist/core/viral-moments.test.js +153 -0
  191. package/dist/core/viral-moments.test.js.map +1 -0
  192. package/dist/core/whisper.d.ts +16 -0
  193. package/dist/core/whisper.d.ts.map +1 -1
  194. package/dist/core/whisper.js +72 -5
  195. package/dist/core/whisper.js.map +1 -1
  196. package/dist/core/whisper.test.js +111 -1
  197. package/dist/core/whisper.test.js.map +1 -1
  198. package/dist/core/youtube-metadata.d.ts +44 -0
  199. package/dist/core/youtube-metadata.d.ts.map +1 -0
  200. package/dist/core/youtube-metadata.js +168 -0
  201. package/dist/core/youtube-metadata.js.map +1 -0
  202. package/dist/core/youtube-metadata.test.d.ts +2 -0
  203. package/dist/core/youtube-metadata.test.d.ts.map +1 -0
  204. package/dist/core/youtube-metadata.test.js +132 -0
  205. package/dist/core/youtube-metadata.test.js.map +1 -0
  206. package/dist/index.d.ts +1 -1
  207. package/dist/index.d.ts.map +1 -1
  208. package/dist/index.js +3 -2
  209. package/dist/index.js.map +1 -1
  210. package/dist/prompt-commands.d.ts +24 -0
  211. package/dist/prompt-commands.d.ts.map +1 -0
  212. package/dist/prompt-commands.js +243 -0
  213. package/dist/prompt-commands.js.map +1 -0
  214. package/dist/prompt-commands.test.d.ts +2 -0
  215. package/dist/prompt-commands.test.d.ts.map +1 -0
  216. package/dist/prompt-commands.test.js +46 -0
  217. package/dist/prompt-commands.test.js.map +1 -0
  218. package/dist/skills.d.ts +6 -6
  219. package/dist/skills.d.ts.map +1 -1
  220. package/dist/skills.js +1426 -445
  221. package/dist/skills.js.map +1 -1
  222. package/dist/system-prompt.d.ts.map +1 -1
  223. package/dist/system-prompt.js +108 -0
  224. package/dist/system-prompt.js.map +1 -1
  225. package/dist/tools/add-fades.d.ts.map +1 -1
  226. package/dist/tools/add-fades.js +2 -1
  227. package/dist/tools/add-fades.js.map +1 -1
  228. package/dist/tools/add-sfx-at-cuts.d.ts.map +1 -1
  229. package/dist/tools/add-sfx-at-cuts.js +36 -11
  230. package/dist/tools/add-sfx-at-cuts.js.map +1 -1
  231. package/dist/tools/add-sfx-to-timeline.d.ts +34 -0
  232. package/dist/tools/add-sfx-to-timeline.d.ts.map +1 -0
  233. package/dist/tools/add-sfx-to-timeline.js +169 -0
  234. package/dist/tools/add-sfx-to-timeline.js.map +1 -0
  235. package/dist/tools/add-sfx-to-timeline.test.d.ts +2 -0
  236. package/dist/tools/add-sfx-to-timeline.test.d.ts.map +1 -0
  237. package/dist/tools/add-sfx-to-timeline.test.js +181 -0
  238. package/dist/tools/add-sfx-to-timeline.test.js.map +1 -0
  239. package/dist/tools/audit-first-frame.d.ts +36 -0
  240. package/dist/tools/audit-first-frame.d.ts.map +1 -0
  241. package/dist/tools/audit-first-frame.js +181 -0
  242. package/dist/tools/audit-first-frame.js.map +1 -0
  243. package/dist/tools/audit-retention-structure.d.ts +20 -0
  244. package/dist/tools/audit-retention-structure.d.ts.map +1 -0
  245. package/dist/tools/audit-retention-structure.js +95 -0
  246. package/dist/tools/audit-retention-structure.js.map +1 -0
  247. package/dist/tools/audit-retention-structure.test.d.ts +2 -0
  248. package/dist/tools/audit-retention-structure.test.d.ts.map +1 -0
  249. package/dist/tools/audit-retention-structure.test.js +93 -0
  250. package/dist/tools/audit-retention-structure.test.js.map +1 -0
  251. package/dist/tools/bleep-words.d.ts +59 -0
  252. package/dist/tools/bleep-words.d.ts.map +1 -0
  253. package/dist/tools/bleep-words.js +211 -0
  254. package/dist/tools/bleep-words.js.map +1 -0
  255. package/dist/tools/bleep-words.test.d.ts +2 -0
  256. package/dist/tools/bleep-words.test.d.ts.map +1 -0
  257. package/dist/tools/bleep-words.test.js +96 -0
  258. package/dist/tools/bleep-words.test.js.map +1 -0
  259. package/dist/tools/burn-subtitles.d.ts.map +1 -1
  260. package/dist/tools/burn-subtitles.js +10 -5
  261. package/dist/tools/burn-subtitles.js.map +1 -1
  262. package/dist/tools/clean-audio.d.ts.map +1 -1
  263. package/dist/tools/clean-audio.js +2 -1
  264. package/dist/tools/clean-audio.js.map +1 -1
  265. package/dist/tools/cluster-takes.js +2 -1
  266. package/dist/tools/cluster-takes.js.map +1 -1
  267. package/dist/tools/compose-thumbnail-variants.d.ts +70 -0
  268. package/dist/tools/compose-thumbnail-variants.d.ts.map +1 -0
  269. package/dist/tools/compose-thumbnail-variants.js +274 -0
  270. package/dist/tools/compose-thumbnail-variants.js.map +1 -0
  271. package/dist/tools/compose-thumbnail.d.ts +6 -13
  272. package/dist/tools/compose-thumbnail.d.ts.map +1 -1
  273. package/dist/tools/compose-thumbnail.js +44 -81
  274. package/dist/tools/compose-thumbnail.js.map +1 -1
  275. package/dist/tools/concat-videos.d.ts.map +1 -1
  276. package/dist/tools/concat-videos.js +12 -5
  277. package/dist/tools/concat-videos.js.map +1 -1
  278. package/dist/tools/concat-videos.test.d.ts +2 -0
  279. package/dist/tools/concat-videos.test.d.ts.map +1 -0
  280. package/dist/tools/concat-videos.test.js +103 -0
  281. package/dist/tools/concat-videos.test.js.map +1 -0
  282. package/dist/tools/crossfade-videos.d.ts.map +1 -1
  283. package/dist/tools/crossfade-videos.js +2 -1
  284. package/dist/tools/crossfade-videos.js.map +1 -1
  285. package/dist/tools/cut-filler-words.d.ts.map +1 -1
  286. package/dist/tools/cut-filler-words.js +24 -8
  287. package/dist/tools/cut-filler-words.js.map +1 -1
  288. package/dist/tools/detect-speaker-changes.js +2 -1
  289. package/dist/tools/detect-speaker-changes.js.map +1 -1
  290. package/dist/tools/extract-audio.d.ts.map +1 -1
  291. package/dist/tools/extract-audio.js +13 -7
  292. package/dist/tools/extract-audio.js.map +1 -1
  293. package/dist/tools/face-reframe.d.ts +30 -0
  294. package/dist/tools/face-reframe.d.ts.map +1 -0
  295. package/dist/tools/face-reframe.js +143 -0
  296. package/dist/tools/face-reframe.js.map +1 -0
  297. package/dist/tools/face-reframe.test.d.ts +2 -0
  298. package/dist/tools/face-reframe.test.d.ts.map +1 -0
  299. package/dist/tools/face-reframe.test.js +139 -0
  300. package/dist/tools/face-reframe.test.js.map +1 -0
  301. package/dist/tools/find-viral-moments.d.ts +23 -0
  302. package/dist/tools/find-viral-moments.d.ts.map +1 -0
  303. package/dist/tools/find-viral-moments.js +176 -0
  304. package/dist/tools/find-viral-moments.js.map +1 -0
  305. package/dist/tools/find-viral-moments.test.d.ts +2 -0
  306. package/dist/tools/find-viral-moments.test.d.ts.map +1 -0
  307. package/dist/tools/find-viral-moments.test.js +144 -0
  308. package/dist/tools/find-viral-moments.test.js.map +1 -0
  309. package/dist/tools/generate-gif.d.ts.map +1 -1
  310. package/dist/tools/generate-gif.js +47 -40
  311. package/dist/tools/generate-gif.js.map +1 -1
  312. package/dist/tools/generate-gif.test.d.ts +2 -0
  313. package/dist/tools/generate-gif.test.d.ts.map +1 -0
  314. package/dist/tools/generate-gif.test.js +115 -0
  315. package/dist/tools/generate-gif.test.js.map +1 -0
  316. package/dist/tools/generate-outro.d.ts +18 -0
  317. package/dist/tools/generate-outro.d.ts.map +1 -0
  318. package/dist/tools/generate-outro.js +175 -0
  319. package/dist/tools/generate-outro.js.map +1 -0
  320. package/dist/tools/generate-youtube-metadata.d.ts +23 -0
  321. package/dist/tools/generate-youtube-metadata.d.ts.map +1 -0
  322. package/dist/tools/generate-youtube-metadata.js +103 -0
  323. package/dist/tools/generate-youtube-metadata.js.map +1 -0
  324. package/dist/tools/generate-youtube-metadata.test.d.ts +2 -0
  325. package/dist/tools/generate-youtube-metadata.test.d.ts.map +1 -0
  326. package/dist/tools/generate-youtube-metadata.test.js +118 -0
  327. package/dist/tools/generate-youtube-metadata.test.js.map +1 -0
  328. package/dist/tools/index.d.ts +14 -0
  329. package/dist/tools/index.d.ts.map +1 -1
  330. package/dist/tools/index.js +130 -1
  331. package/dist/tools/index.js.map +1 -1
  332. package/dist/tools/index.test.js +27 -1
  333. package/dist/tools/index.test.js.map +1 -1
  334. package/dist/tools/ken-burns.d.ts.map +1 -1
  335. package/dist/tools/ken-burns.js +2 -1
  336. package/dist/tools/ken-burns.js.map +1 -1
  337. package/dist/tools/loop-match-short.d.ts +22 -0
  338. package/dist/tools/loop-match-short.d.ts.map +1 -0
  339. package/dist/tools/loop-match-short.js +107 -0
  340. package/dist/tools/loop-match-short.js.map +1 -0
  341. package/dist/tools/mix-audio.d.ts.map +1 -1
  342. package/dist/tools/mix-audio.js +2 -1
  343. package/dist/tools/mix-audio.js.map +1 -1
  344. package/dist/tools/normalize-loudness.d.ts.map +1 -1
  345. package/dist/tools/normalize-loudness.js +2 -1
  346. package/dist/tools/normalize-loudness.js.map +1 -1
  347. package/dist/tools/path-traversal.test.d.ts +15 -0
  348. package/dist/tools/path-traversal.test.d.ts.map +1 -0
  349. package/dist/tools/path-traversal.test.js +223 -0
  350. package/dist/tools/path-traversal.test.js.map +1 -0
  351. package/dist/tools/pick-best-takes.js +2 -1
  352. package/dist/tools/pick-best-takes.js.map +1 -1
  353. package/dist/tools/punch-in.d.ts.map +1 -1
  354. package/dist/tools/punch-in.js +2 -1
  355. package/dist/tools/punch-in.js.map +1 -1
  356. package/dist/tools/read-transcript.js +2 -1
  357. package/dist/tools/read-transcript.js.map +1 -1
  358. package/dist/tools/render-multi-format.d.ts +35 -0
  359. package/dist/tools/render-multi-format.d.ts.map +1 -0
  360. package/dist/tools/render-multi-format.js +206 -0
  361. package/dist/tools/render-multi-format.js.map +1 -0
  362. package/dist/tools/render-multi-format.test.d.ts +2 -0
  363. package/dist/tools/render-multi-format.test.d.ts.map +1 -0
  364. package/dist/tools/render-multi-format.test.js +312 -0
  365. package/dist/tools/render-multi-format.test.js.map +1 -0
  366. package/dist/tools/render.d.ts.map +1 -1
  367. package/dist/tools/render.js +2 -2
  368. package/dist/tools/render.js.map +1 -1
  369. package/dist/tools/rewrite-hook.d.ts +32 -0
  370. package/dist/tools/rewrite-hook.d.ts.map +1 -0
  371. package/dist/tools/rewrite-hook.js +65 -0
  372. package/dist/tools/rewrite-hook.js.map +1 -0
  373. package/dist/tools/score-clip.d.ts +30 -0
  374. package/dist/tools/score-clip.d.ts.map +1 -0
  375. package/dist/tools/score-clip.js +109 -0
  376. package/dist/tools/score-clip.js.map +1 -0
  377. package/dist/tools/score-clip.test.d.ts +2 -0
  378. package/dist/tools/score-clip.test.d.ts.map +1 -0
  379. package/dist/tools/score-clip.test.js +110 -0
  380. package/dist/tools/score-clip.test.js.map +1 -0
  381. package/dist/tools/search-tools.d.ts +34 -0
  382. package/dist/tools/search-tools.d.ts.map +1 -0
  383. package/dist/tools/search-tools.js +86 -0
  384. package/dist/tools/search-tools.js.map +1 -0
  385. package/dist/tools/search-tools.test.d.ts +2 -0
  386. package/dist/tools/search-tools.test.d.ts.map +1 -0
  387. package/dist/tools/search-tools.test.js +60 -0
  388. package/dist/tools/search-tools.test.js.map +1 -0
  389. package/dist/tools/snap-cuts-to-beats.d.ts +18 -0
  390. package/dist/tools/snap-cuts-to-beats.d.ts.map +1 -0
  391. package/dist/tools/snap-cuts-to-beats.js +110 -0
  392. package/dist/tools/snap-cuts-to-beats.js.map +1 -0
  393. package/dist/tools/snap-cuts-to-beats.test.d.ts +2 -0
  394. package/dist/tools/snap-cuts-to-beats.test.d.ts.map +1 -0
  395. package/dist/tools/snap-cuts-to-beats.test.js +99 -0
  396. package/dist/tools/snap-cuts-to-beats.test.js.map +1 -0
  397. package/dist/tools/speed-ramp.d.ts.map +1 -1
  398. package/dist/tools/speed-ramp.js +2 -1
  399. package/dist/tools/speed-ramp.js.map +1 -1
  400. package/dist/tools/stabilize-video.d.ts.map +1 -1
  401. package/dist/tools/stabilize-video.js +2 -1
  402. package/dist/tools/stabilize-video.js.map +1 -1
  403. package/dist/tools/suggest-broll.d.ts +34 -0
  404. package/dist/tools/suggest-broll.d.ts.map +1 -0
  405. package/dist/tools/suggest-broll.js +367 -0
  406. package/dist/tools/suggest-broll.js.map +1 -0
  407. package/dist/tools/suggest-broll.test.d.ts +2 -0
  408. package/dist/tools/suggest-broll.test.d.ts.map +1 -0
  409. package/dist/tools/suggest-broll.test.js +217 -0
  410. package/dist/tools/suggest-broll.test.js.map +1 -0
  411. package/dist/tools/text-based-cut.d.ts +33 -0
  412. package/dist/tools/text-based-cut.d.ts.map +1 -0
  413. package/dist/tools/text-based-cut.js +172 -0
  414. package/dist/tools/text-based-cut.js.map +1 -0
  415. package/dist/tools/text-based-cut.test.d.ts +2 -0
  416. package/dist/tools/text-based-cut.test.d.ts.map +1 -0
  417. package/dist/tools/text-based-cut.test.js +32 -0
  418. package/dist/tools/text-based-cut.test.js.map +1 -0
  419. package/dist/tools/transcribe.d.ts +1 -1
  420. package/dist/tools/transition-videos.d.ts +1 -1
  421. package/dist/tools/transition-videos.d.ts.map +1 -1
  422. package/dist/tools/transition-videos.js +2 -1
  423. package/dist/tools/transition-videos.js.map +1 -1
  424. package/dist/tools/trim-dead-air.d.ts +59 -0
  425. package/dist/tools/trim-dead-air.d.ts.map +1 -0
  426. package/dist/tools/trim-dead-air.js +215 -0
  427. package/dist/tools/trim-dead-air.js.map +1 -0
  428. package/dist/tools/trim-dead-air.test.d.ts +2 -0
  429. package/dist/tools/trim-dead-air.test.d.ts.map +1 -0
  430. package/dist/tools/trim-dead-air.test.js +75 -0
  431. package/dist/tools/trim-dead-air.test.js.map +1 -0
  432. package/dist/tools/verify-thumbnail-promise.d.ts +33 -0
  433. package/dist/tools/verify-thumbnail-promise.d.ts.map +1 -0
  434. package/dist/tools/verify-thumbnail-promise.js +112 -0
  435. package/dist/tools/verify-thumbnail-promise.js.map +1 -0
  436. package/dist/tools/verify-thumbnail-promise.test.d.ts +2 -0
  437. package/dist/tools/verify-thumbnail-promise.test.d.ts.map +1 -0
  438. package/dist/tools/verify-thumbnail-promise.test.js +38 -0
  439. package/dist/tools/verify-thumbnail-promise.test.js.map +1 -0
  440. package/dist/tools/write-keyword-captions.d.ts +7 -0
  441. package/dist/tools/write-keyword-captions.d.ts.map +1 -1
  442. package/dist/tools/write-keyword-captions.js +35 -4
  443. package/dist/tools/write-keyword-captions.js.map +1 -1
  444. package/dist/ui/App.d.ts.map +1 -1
  445. package/dist/ui/App.js +75 -11
  446. package/dist/ui/App.js.map +1 -1
  447. package/dist/ui/tool-formatters.d.ts +30 -0
  448. package/dist/ui/tool-formatters.d.ts.map +1 -0
  449. package/dist/ui/tool-formatters.js +461 -0
  450. package/dist/ui/tool-formatters.js.map +1 -0
  451. package/dist/ui/tool-formatters.test.d.ts +2 -0
  452. package/dist/ui/tool-formatters.test.d.ts.map +1 -0
  453. package/dist/ui/tool-formatters.test.js +143 -0
  454. package/dist/ui/tool-formatters.test.js.map +1 -0
  455. package/package.json +10 -9
@@ -0,0 +1,218 @@
1
+ /**
2
+ * Bundled sound-effect library.
3
+ *
4
+ * gg-editor's `add_sfx_at_cuts` needs a WAV/MP3 file per hit. Without this
5
+ * module the agent had to either ask the user to supply one or invent a path
6
+ * — both broke the canonical retention pipeline. Now the agent calls
7
+ * `add_sfx_at_cuts(sfx="whoosh", …)` with a bundled name and the resolver
8
+ * either returns a cached WAV or synthesises one via ffmpeg into
9
+ * `~/.gg/sfx-cache/<name>.wav` (~50 ms one-time per name; cached forever).
10
+ *
11
+ * Why synthesised, not shipped: no third-party files = no IP risk + no npm
12
+ * weight. Every recipe is a pure ffmpeg-lavfi expression so the output is
13
+ * deterministic and reproducible. Quality is "creator-vlog acceptable" —
14
+ * not Skywalker Sound, but indistinguishable from typical CapCut presets in
15
+ * a sub-1-second cut.
16
+ *
17
+ * Design rules for recipes:
18
+ * - Stereo (`-ac 2`) — every consumer mixes into stereo audio.
19
+ * - 200–500 ms long. Anything longer fights the next cut.
20
+ * - Peak ≤ -3 dBFS (the recipes all keep amplitude ≤ 0.7) so
21
+ * `add_sfx_at_cuts`'s default -8 dB gain leaves headroom for voice.
22
+ * - Linear fade in (≥5 ms) + fade out — no clicks at the envelope edges.
23
+ */
24
+ import { existsSync, mkdirSync } from "node:fs";
25
+ import { homedir } from "node:os";
26
+ import { join, resolve as resolvePath } from "node:path";
27
+ import { runFfmpeg } from "./media/ffmpeg.js";
28
+ /**
29
+ * The bundled set. Keep this list short and creator-relevant —
30
+ * 8–10 names covers the entire short-form vocabulary.
31
+ */
32
+ export const BUNDLED_SFX = {
33
+ pop: {
34
+ description: "Short upward blip — punctuates a cut without dominating",
35
+ args: [
36
+ "-f",
37
+ "lavfi",
38
+ "-i",
39
+ "sine=frequency=880:duration=0.08",
40
+ "-af",
41
+ "afade=t=in:d=0.005,afade=t=out:d=0.05:st=0.03,volume=0.5",
42
+ "-ac",
43
+ "2",
44
+ ],
45
+ },
46
+ click: {
47
+ description: "Very short tick — for fast-cut emphasis where pop would be too musical",
48
+ args: [
49
+ "-f",
50
+ "lavfi",
51
+ "-i",
52
+ "anoisesrc=color=white:duration=0.03:amplitude=0.6",
53
+ "-af",
54
+ "bandpass=frequency=4000:width_type=h:w=3000,afade=t=in:d=0.002,afade=t=out:d=0.025:st=0.005,volume=2",
55
+ "-ac",
56
+ "2",
57
+ ],
58
+ },
59
+ whoosh: {
60
+ description: "Downward frequency sweep — classic vlog cut effect",
61
+ args: [
62
+ "-f",
63
+ "lavfi",
64
+ // Linear sweep 3000 Hz → ~200 Hz over 0.3 s (phase = 2π·(f₀·t + ½·k·t²))
65
+ "-i",
66
+ "aevalsrc='0.4*sin(2*PI*(3000*t-4667*t*t))':d=0.3",
67
+ "-af",
68
+ "afade=t=in:d=0.05,afade=t=out:d=0.1:st=0.2",
69
+ "-ac",
70
+ "2",
71
+ ],
72
+ },
73
+ "whoosh-up": {
74
+ description: "Upward frequency sweep — for reveals / 'lift' moments",
75
+ args: [
76
+ "-f",
77
+ "lavfi",
78
+ // Sweep 200 Hz → ~3000 Hz over 0.3 s
79
+ "-i",
80
+ "aevalsrc='0.4*sin(2*PI*(200*t+4667*t*t))':d=0.3",
81
+ "-af",
82
+ "afade=t=in:d=0.05,afade=t=out:d=0.1:st=0.2",
83
+ "-ac",
84
+ "2",
85
+ ],
86
+ },
87
+ swoosh: {
88
+ description: "Filtered noise sweep — softer/airier than whoosh, good for transitions",
89
+ args: [
90
+ "-f",
91
+ "lavfi",
92
+ "-i",
93
+ "anoisesrc=color=brown:duration=0.4:amplitude=0.5",
94
+ "-af",
95
+ "bandpass=frequency=1500:width_type=h:w=2500,afade=t=in:d=0.05,afade=t=out:d=0.15:st=0.25,volume=2.5",
96
+ "-ac",
97
+ "2",
98
+ ],
99
+ },
100
+ riser: {
101
+ description: "600 ms sine sweep up — builds tension before a reveal",
102
+ args: [
103
+ "-f",
104
+ "lavfi",
105
+ // Sweep 200 → ~2200 Hz over 0.6 s with rising amplitude
106
+ "-i",
107
+ "aevalsrc='(0.2+0.4*t)*sin(2*PI*(200*t+1667*t*t))':d=0.6",
108
+ "-af",
109
+ "afade=t=in:d=0.1,afade=t=out:d=0.05:st=0.55",
110
+ "-ac",
111
+ "2",
112
+ ],
113
+ },
114
+ "bass-drop": {
115
+ description: "Sub bass with downward sweep — for big-reveal cuts (use sparingly)",
116
+ args: [
117
+ "-f",
118
+ "lavfi",
119
+ // Sweep 120 Hz → ~40 Hz over 0.45 s
120
+ "-i",
121
+ "aevalsrc='0.6*sin(2*PI*(120*t-89*t*t))':d=0.45",
122
+ "-af",
123
+ "afade=t=in:d=0.02,afade=t=out:d=0.15:st=0.3",
124
+ "-ac",
125
+ "2",
126
+ ],
127
+ },
128
+ ding: {
129
+ description: "Soft bell — for highlighting an on-screen item / list bullet",
130
+ args: [
131
+ "-f",
132
+ "lavfi",
133
+ "-i",
134
+ // Two-harmonic bell: fundamental + perfect fifth, exponential decay
135
+ "aevalsrc='exp(-4*t)*(0.5*sin(2*PI*1318*t)+0.25*sin(2*PI*1976*t))':d=0.5",
136
+ "-af",
137
+ "afade=t=in:d=0.005",
138
+ "-ac",
139
+ "2",
140
+ ],
141
+ },
142
+ thump: {
143
+ description: "Low percussive hit — landing impact, soft punch",
144
+ args: [
145
+ "-f",
146
+ "lavfi",
147
+ "-i",
148
+ // Damped sine ~80 Hz with fast decay
149
+ "aevalsrc='exp(-12*t)*0.7*sin(2*PI*80*t)':d=0.25",
150
+ "-af",
151
+ "afade=t=in:d=0.005",
152
+ "-ac",
153
+ "2",
154
+ ],
155
+ },
156
+ };
157
+ /** Names of every bundled SFX (sorted for stable display). */
158
+ export function listBundledSfxNames() {
159
+ return Object.keys(BUNDLED_SFX).sort();
160
+ }
161
+ /** A short markdown-style list of bundled SFX for tool descriptions / system prompts. */
162
+ export function bundledSfxDescriptionList() {
163
+ return listBundledSfxNames()
164
+ .map((n) => `${n} (${BUNDLED_SFX[n].description})`)
165
+ .join(", ");
166
+ }
167
+ /** Cache dir for synthesised WAVs. Stable across sessions, gitignored by convention. */
168
+ export function getSfxCacheDir() {
169
+ return resolvePath(homedir(), ".gg", "sfx-cache");
170
+ }
171
+ /**
172
+ * Resolve an SFX argument:
173
+ *
174
+ * - Bundled name (e.g. `"whoosh"`) → cache hit at `~/.gg/sfx-cache/whoosh.wav`,
175
+ * synthesising via ffmpeg on first request.
176
+ * - File-like string (contains `/`, `\`, `.`, `~`) → resolve against `cwd`
177
+ * and return as-is. The caller still has to verify the file exists.
178
+ *
179
+ * Throws when the name is neither bundled nor file-like.
180
+ */
181
+ export async function resolveSfx(nameOrPath, cwd, signal) {
182
+ // Bundled names are bare alphanumeric+hyphen tokens with no path / extension.
183
+ // Any string with `/`, `\`, `.`, or `~` is a file path.
184
+ const looksLikePath = /[/\\.~]/.test(nameOrPath);
185
+ if (!looksLikePath) {
186
+ const recipe = BUNDLED_SFX[nameOrPath];
187
+ if (!recipe) {
188
+ throw new Error(`unknown SFX name: '${nameOrPath}'. Bundled: ${listBundledSfxNames().join(", ")}`);
189
+ }
190
+ const path = await ensureBundledSfx(nameOrPath, signal);
191
+ return { path, bundled: true, name: nameOrPath };
192
+ }
193
+ // Treat as a file path.
194
+ return { path: resolvePath(cwd, nameOrPath), bundled: false };
195
+ }
196
+ /**
197
+ * Ensure a bundled SFX is present on disk, synthesising it via ffmpeg if not.
198
+ * Returns the absolute cache path.
199
+ */
200
+ export async function ensureBundledSfx(name, signal) {
201
+ const recipe = BUNDLED_SFX[name];
202
+ if (!recipe) {
203
+ throw new Error(`unknown bundled SFX: '${name}'`);
204
+ }
205
+ const dir = getSfxCacheDir();
206
+ mkdirSync(dir, { recursive: true });
207
+ const out = join(dir, `${name}.wav`);
208
+ if (existsSync(out))
209
+ return out;
210
+ const ffArgs = [...recipe.args, out];
211
+ const r = await runFfmpeg(ffArgs, { signal });
212
+ if (r.code !== 0) {
213
+ const tail = r.stderr.split("\n").filter(Boolean).slice(-3).join(" | ").slice(-300);
214
+ throw new Error(`ffmpeg failed synthesising '${name}' SFX: ${tail}`);
215
+ }
216
+ return out;
217
+ }
218
+ //# sourceMappingURL=bundled-sfx.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bundled-sfx.js","sourceRoot":"","sources":["../../src/core/bundled-sfx.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=bundled-sfx.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bundled-sfx.test.d.ts","sourceRoot":"","sources":["../../src/core/bundled-sfx.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,81 @@
1
+ import { describe, expect, it, vi } from "vitest";
2
+ import { BUNDLED_SFX, bundledSfxDescriptionList, ensureBundledSfx, getSfxCacheDir, listBundledSfxNames, resolveSfx, } from "./bundled-sfx.js";
3
+ // We don't want any of these tests touching real ffmpeg or the user's home
4
+ // directory. Most assertions are pure-function. The synthesis tests mock
5
+ // runFfmpeg via vi.mock at module scope.
6
+ vi.mock("./media/ffmpeg.js", async () => {
7
+ // eslint-disable-next-line @typescript-eslint/consistent-type-imports
8
+ const real = await vi.importActual("./media/ffmpeg.js");
9
+ return {
10
+ ...real,
11
+ runFfmpeg: vi.fn(async () => ({ code: 0, stdout: "", stderr: "" })),
12
+ };
13
+ });
14
+ describe("BUNDLED_SFX registry", () => {
15
+ it("ships at least 8 named effects covering the creator vocabulary", () => {
16
+ const names = listBundledSfxNames();
17
+ for (const required of ["pop", "click", "whoosh", "swoosh", "riser", "thump", "ding"]) {
18
+ expect(names).toContain(required);
19
+ }
20
+ expect(names.length).toBeGreaterThanOrEqual(8);
21
+ });
22
+ it("every recipe has stereo + non-empty args + no embedded output path", () => {
23
+ for (const [_name, recipe] of Object.entries(BUNDLED_SFX)) {
24
+ expect(recipe.description.length).toBeGreaterThan(10);
25
+ expect(recipe.args).toContain("-ac");
26
+ const acIdx = recipe.args.indexOf("-ac");
27
+ expect(recipe.args[acIdx + 1]).toBe("2");
28
+ // Output path is appended at synthesis time, not embedded in recipes.
29
+ const lastArg = recipe.args[recipe.args.length - 1];
30
+ expect(lastArg.endsWith(".wav")).toBe(false);
31
+ }
32
+ });
33
+ it("bundledSfxDescriptionList includes every name + description", () => {
34
+ const list = bundledSfxDescriptionList();
35
+ for (const name of listBundledSfxNames()) {
36
+ expect(list).toContain(name);
37
+ }
38
+ expect(list).toContain("(");
39
+ expect(list).toContain(")");
40
+ });
41
+ });
42
+ describe("getSfxCacheDir", () => {
43
+ it("returns a path under the user's home", () => {
44
+ const dir = getSfxCacheDir();
45
+ expect(dir).toContain(".gg");
46
+ expect(dir).toContain("sfx-cache");
47
+ });
48
+ });
49
+ describe("resolveSfx", () => {
50
+ it("treats unambiguous bundled names as bundled", async () => {
51
+ // Force ffmpeg mock to claim success without touching disk.
52
+ const r = await resolveSfx("whoosh", "/tmp/x").catch((e) => ({ error: e.message }));
53
+ if ("error" in r) {
54
+ // Expected if cache mkdir somehow fails on the test runner — skip.
55
+ return;
56
+ }
57
+ expect(r.bundled).toBe(true);
58
+ expect(r.name).toBe("whoosh");
59
+ expect(r.path).toMatch(/whoosh\.wav$/);
60
+ });
61
+ it("treats anything containing a path separator as a file path", async () => {
62
+ const r = await resolveSfx("./assets/myfx.wav", "/home/user");
63
+ expect(r.bundled).toBe(false);
64
+ expect(r.path).toBe("/home/user/assets/myfx.wav");
65
+ expect(r.name).toBeUndefined();
66
+ });
67
+ it("treats anything with a dot (extension) as a file path", async () => {
68
+ const r = await resolveSfx("custom.mp3", "/home/user");
69
+ expect(r.bundled).toBe(false);
70
+ expect(r.path).toBe("/home/user/custom.mp3");
71
+ });
72
+ it("rejects unknown bare names with the bundled list in the message", async () => {
73
+ await expect(resolveSfx("notarealsfx", "/tmp")).rejects.toThrow(/Bundled:.*whoosh/);
74
+ });
75
+ });
76
+ describe("ensureBundledSfx", () => {
77
+ it("rejects unknown names", async () => {
78
+ await expect(ensureBundledSfx("notarealsfx")).rejects.toThrow(/unknown bundled SFX/);
79
+ });
80
+ });
81
+ //# sourceMappingURL=bundled-sfx.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bundled-sfx.test.js","sourceRoot":"","sources":["../../src/core/bundled-sfx.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,57 @@
1
+ /**
2
+ * Shared abort wiring for spawned child processes.
3
+ *
4
+ * Every long-running tool in gg-editor — ffmpeg, whisper.cpp, whisperx,
5
+ * Python sidecars (librosa beats, MediaPipe face reframe), Resolve bridge —
6
+ * is wrapped in a Promise that resolves on `child.on("close")`. Without
7
+ * careful abort handling those promises can hang for the duration of the
8
+ * child's actual exit even after the user pressed ESC.
9
+ *
10
+ * The naive pattern `signal.addEventListener("abort", () => child.kill())`
11
+ * has three real failure modes:
12
+ *
13
+ * 1. **Pre-abort race** — if `signal.aborted` is already true at attach
14
+ * time, the listener never fires (Node only dispatches `abort` once,
15
+ * at the moment the controller flips). The child runs to completion.
16
+ *
17
+ * 2. **SIGTERM ignored** — CPU-bound inference (whisper.cpp processing a
18
+ * 30-min audio chunk, ffmpeg encoding a long video) can ignore
19
+ * SIGTERM until it finishes the current loop iteration. Users press
20
+ * ESC and watch nothing happen for many seconds.
21
+ *
22
+ * 3. **Promise waits for close** — even if the kill works, the outer
23
+ * Promise resolves on `child.on("close")`, which fires AFTER the
24
+ * child finishes dying. The agent loop sees the abort late.
25
+ *
26
+ * `wireChildAbort` fixes all three:
27
+ *
28
+ * - Synchronous pre-aborted check → kills + rejects immediately.
29
+ * - SIGTERM, then SIGKILL after a configurable grace period (default 1.5s).
30
+ * - Caller decides whether to reject early (most callers pass `onAbort`
31
+ * that rejects with an `AbortError`); resolves can still happen
32
+ * naturally on close if the caller prefers a "code: SIGTERM" outcome.
33
+ * - Returns a cleanup function that removes the listener and clears the
34
+ * SIGKILL timer. Caller must invoke once the promise settles.
35
+ */
36
+ import type { ChildProcess } from "node:child_process";
37
+ /**
38
+ * Standard AbortError. Mirrors what `fetch` raises when its signal fires —
39
+ * `name === "AbortError"` is the cross-platform contract for cancellation.
40
+ */
41
+ export declare function abortError(message?: string): Error;
42
+ export interface WireChildAbortOptions {
43
+ /** Optional. Called on abort BEFORE the SIGKILL grace period. Use to reject the wrapping Promise immediately. */
44
+ onAbort?: () => void;
45
+ /** Grace period before escalating to SIGKILL. Default 1500 ms. */
46
+ killAfterMs?: number;
47
+ }
48
+ /**
49
+ * Wire an AbortSignal to a child process. See file header for design rationale.
50
+ *
51
+ * Returns a cleanup function the caller MUST invoke once the promise settles
52
+ * (in both `child.on("close")` and `child.on("error")`). Without cleanup the
53
+ * abort listener stays on the signal and the SIGKILL timer keeps a process
54
+ * reference alive.
55
+ */
56
+ export declare function wireChildAbort(signal: AbortSignal | undefined, child: ChildProcess, opts?: WireChildAbortOptions): () => void;
57
+ //# sourceMappingURL=child-abort.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"child-abort.d.ts","sourceRoot":"","sources":["../../src/core/child-abort.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAEvD;;;GAGG;AACH,wBAAgB,UAAU,CAAC,OAAO,SAAY,GAAG,KAAK,CAOrD;AAED,MAAM,WAAW,qBAAqB;IACpC,iHAAiH;IACjH,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,kEAAkE;IAClE,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED;;;;;;;GAOG;AACH,wBAAgB,cAAc,CAC5B,MAAM,EAAE,WAAW,GAAG,SAAS,EAC/B,KAAK,EAAE,YAAY,EACnB,IAAI,GAAE,qBAA0B,GAC/B,MAAM,IAAI,CAoCZ"}
@@ -0,0 +1,95 @@
1
+ /**
2
+ * Shared abort wiring for spawned child processes.
3
+ *
4
+ * Every long-running tool in gg-editor — ffmpeg, whisper.cpp, whisperx,
5
+ * Python sidecars (librosa beats, MediaPipe face reframe), Resolve bridge —
6
+ * is wrapped in a Promise that resolves on `child.on("close")`. Without
7
+ * careful abort handling those promises can hang for the duration of the
8
+ * child's actual exit even after the user pressed ESC.
9
+ *
10
+ * The naive pattern `signal.addEventListener("abort", () => child.kill())`
11
+ * has three real failure modes:
12
+ *
13
+ * 1. **Pre-abort race** — if `signal.aborted` is already true at attach
14
+ * time, the listener never fires (Node only dispatches `abort` once,
15
+ * at the moment the controller flips). The child runs to completion.
16
+ *
17
+ * 2. **SIGTERM ignored** — CPU-bound inference (whisper.cpp processing a
18
+ * 30-min audio chunk, ffmpeg encoding a long video) can ignore
19
+ * SIGTERM until it finishes the current loop iteration. Users press
20
+ * ESC and watch nothing happen for many seconds.
21
+ *
22
+ * 3. **Promise waits for close** — even if the kill works, the outer
23
+ * Promise resolves on `child.on("close")`, which fires AFTER the
24
+ * child finishes dying. The agent loop sees the abort late.
25
+ *
26
+ * `wireChildAbort` fixes all three:
27
+ *
28
+ * - Synchronous pre-aborted check → kills + rejects immediately.
29
+ * - SIGTERM, then SIGKILL after a configurable grace period (default 1.5s).
30
+ * - Caller decides whether to reject early (most callers pass `onAbort`
31
+ * that rejects with an `AbortError`); resolves can still happen
32
+ * naturally on close if the caller prefers a "code: SIGTERM" outcome.
33
+ * - Returns a cleanup function that removes the listener and clears the
34
+ * SIGKILL timer. Caller must invoke once the promise settles.
35
+ */
36
+ /**
37
+ * Standard AbortError. Mirrors what `fetch` raises when its signal fires —
38
+ * `name === "AbortError"` is the cross-platform contract for cancellation.
39
+ */
40
+ export function abortError(message = "aborted") {
41
+ if (typeof DOMException !== "undefined") {
42
+ return new DOMException(message, "AbortError");
43
+ }
44
+ const e = new Error(message);
45
+ e.name = "AbortError";
46
+ return e;
47
+ }
48
+ /**
49
+ * Wire an AbortSignal to a child process. See file header for design rationale.
50
+ *
51
+ * Returns a cleanup function the caller MUST invoke once the promise settles
52
+ * (in both `child.on("close")` and `child.on("error")`). Without cleanup the
53
+ * abort listener stays on the signal and the SIGKILL timer keeps a process
54
+ * reference alive.
55
+ */
56
+ export function wireChildAbort(signal, child, opts = {}) {
57
+ if (!signal)
58
+ return () => { };
59
+ const killAfterMs = opts.killAfterMs ?? 1500;
60
+ let killTimer;
61
+ let fired = false;
62
+ const onAbort = () => {
63
+ if (fired)
64
+ return;
65
+ fired = true;
66
+ try {
67
+ child.kill("SIGTERM");
68
+ }
69
+ catch {
70
+ // child may already be dead — fine.
71
+ }
72
+ killTimer = setTimeout(() => {
73
+ try {
74
+ child.kill("SIGKILL");
75
+ }
76
+ catch {
77
+ // ditto
78
+ }
79
+ }, killAfterMs);
80
+ opts.onAbort?.();
81
+ };
82
+ if (signal.aborted) {
83
+ // Pre-aborted — fire synchronously so caller's onAbort runs in the same tick.
84
+ onAbort();
85
+ }
86
+ else {
87
+ signal.addEventListener("abort", onAbort, { once: true });
88
+ }
89
+ return () => {
90
+ signal.removeEventListener("abort", onAbort);
91
+ if (killTimer)
92
+ clearTimeout(killTimer);
93
+ };
94
+ }
95
+ //# sourceMappingURL=child-abort.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"child-abort.js","sourceRoot":"","sources":["../../src/core/child-abort.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AAIH;;;GAGG;AACH,MAAM,UAAU,UAAU,CAAC,OAAO,GAAG,SAAS;IAC5C,IAAI,OAAO,YAAY,KAAK,WAAW,EAAE,CAAC;QACxC,OAAO,IAAI,YAAY,CAAC,OAAO,EAAE,YAAY,CAAC,CAAC;IACjD,CAAC;IACD,MAAM,CAAC,GAAG,IAAI,KAAK,CAAC,OAAO,CAAC,CAAC;IAC7B,CAAC,CAAC,IAAI,GAAG,YAAY,CAAC;IACtB,OAAO,CAAC,CAAC;AACX,CAAC;AASD;;;;;;;GAOG;AACH,MAAM,UAAU,cAAc,CAC5B,MAA+B,EAC/B,KAAmB,EACnB,OAA8B,EAAE;IAEhC,IAAI,CAAC,MAAM;QAAE,OAAO,GAAG,EAAE,GAAE,CAAC,CAAC;IAE7B,MAAM,WAAW,GAAG,IAAI,CAAC,WAAW,IAAI,IAAI,CAAC;IAC7C,IAAI,SAAqC,CAAC;IAC1C,IAAI,KAAK,GAAG,KAAK,CAAC;IAElB,MAAM,OAAO,GAAG,GAAG,EAAE;QACnB,IAAI,KAAK;YAAE,OAAO;QAClB,KAAK,GAAG,IAAI,CAAC;QACb,IAAI,CAAC;YACH,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;QACxB,CAAC;QAAC,MAAM,CAAC;YACP,oCAAoC;QACtC,CAAC;QACD,SAAS,GAAG,UAAU,CAAC,GAAG,EAAE;YAC1B,IAAI,CAAC;gBACH,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;YACxB,CAAC;YAAC,MAAM,CAAC;gBACP,QAAQ;YACV,CAAC;QACH,CAAC,EAAE,WAAW,CAAC,CAAC;QAChB,IAAI,CAAC,OAAO,EAAE,EAAE,CAAC;IACnB,CAAC,CAAC;IAEF,IAAI,MAAM,CAAC,OAAO,EAAE,CAAC;QACnB,8EAA8E;QAC9E,OAAO,EAAE,CAAC;IACZ,CAAC;SAAM,CAAC;QACN,MAAM,CAAC,gBAAgB,CAAC,OAAO,EAAE,OAAO,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;IAC5D,CAAC;IAED,OAAO,GAAG,EAAE;QACV,MAAM,CAAC,mBAAmB,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;QAC7C,IAAI,SAAS;YAAE,YAAY,CAAC,SAAS,CAAC,CAAC;IACzC,CAAC,CAAC;AACJ,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=child-abort.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"child-abort.test.d.ts","sourceRoot":"","sources":["../../src/core/child-abort.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,88 @@
1
+ import { spawn } from "node:child_process";
2
+ import { describe, expect, it, vi } from "vitest";
3
+ import { abortError, wireChildAbort } from "./child-abort.js";
4
+ describe("abortError", () => {
5
+ it("returns an error with name 'AbortError'", () => {
6
+ const e = abortError();
7
+ expect(e.name).toBe("AbortError");
8
+ });
9
+ it("accepts a custom message", () => {
10
+ expect(abortError("custom").message).toContain("custom");
11
+ });
12
+ });
13
+ /**
14
+ * Use a child that sleeps long enough that SIGTERM-then-SIGKILL is observable
15
+ * but short enough that test runs stay fast. Node spawned with `-e` running a
16
+ * setTimeout works on every platform CI uses.
17
+ */
18
+ function spawnSleeper(seconds) {
19
+ return spawn(process.execPath, ["-e", `setTimeout(() => {}, ${seconds * 1000})`], {
20
+ stdio: ["ignore", "pipe", "pipe"],
21
+ });
22
+ }
23
+ describe("wireChildAbort", () => {
24
+ it("is a no-op when signal is undefined", () => {
25
+ const child = spawnSleeper(0.1);
26
+ const cleanup = wireChildAbort(undefined, child);
27
+ expect(typeof cleanup).toBe("function");
28
+ cleanup(); // shouldn't throw
29
+ child.kill();
30
+ });
31
+ it("kills the child with SIGTERM when the signal aborts", async () => {
32
+ const ac = new AbortController();
33
+ const child = spawnSleeper(10);
34
+ const killSpy = vi.spyOn(child, "kill");
35
+ const cleanup = wireChildAbort(ac.signal, child);
36
+ ac.abort();
37
+ expect(killSpy).toHaveBeenCalledWith("SIGTERM");
38
+ // Wait for child to actually exit so the test runner doesn't leak.
39
+ await new Promise((resolve) => child.on("close", () => resolve()));
40
+ cleanup();
41
+ });
42
+ it("fires onAbort exactly once even on repeated aborts", () => {
43
+ const ac = new AbortController();
44
+ const child = spawnSleeper(10);
45
+ const onAbort = vi.fn();
46
+ wireChildAbort(ac.signal, child, { onAbort });
47
+ ac.abort();
48
+ ac.abort(); // duplicate signal — listener attached `once`
49
+ expect(onAbort).toHaveBeenCalledTimes(1);
50
+ child.kill("SIGKILL");
51
+ });
52
+ it("fires synchronously when the signal is already aborted at attach time", () => {
53
+ const ac = new AbortController();
54
+ ac.abort(); // pre-abort
55
+ const child = spawnSleeper(10);
56
+ const killSpy = vi.spyOn(child, "kill");
57
+ const onAbort = vi.fn();
58
+ wireChildAbort(ac.signal, child, { onAbort });
59
+ expect(killSpy).toHaveBeenCalledWith("SIGTERM");
60
+ expect(onAbort).toHaveBeenCalledTimes(1);
61
+ child.kill("SIGKILL");
62
+ });
63
+ it("cleanup removes the listener (no further onAbort after cleanup)", () => {
64
+ const ac = new AbortController();
65
+ const child = spawnSleeper(10);
66
+ const onAbort = vi.fn();
67
+ const cleanup = wireChildAbort(ac.signal, child, { onAbort });
68
+ cleanup();
69
+ ac.abort();
70
+ expect(onAbort).not.toHaveBeenCalled();
71
+ child.kill("SIGKILL");
72
+ });
73
+ it("escalates to SIGKILL after the grace period when SIGTERM is ignored", async () => {
74
+ vi.useFakeTimers();
75
+ const ac = new AbortController();
76
+ const child = spawnSleeper(10);
77
+ const killSpy = vi.spyOn(child, "kill");
78
+ wireChildAbort(ac.signal, child, { killAfterMs: 50 });
79
+ ac.abort();
80
+ expect(killSpy).toHaveBeenLastCalledWith("SIGTERM");
81
+ vi.advanceTimersByTime(60);
82
+ expect(killSpy).toHaveBeenLastCalledWith("SIGKILL");
83
+ vi.useRealTimers();
84
+ child.kill("SIGKILL");
85
+ await new Promise((resolve) => child.on("close", () => resolve()));
86
+ });
87
+ });
88
+ //# sourceMappingURL=child-abort.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"child-abort.test.js","sourceRoot":"","sources":["../../src/core/child-abort.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,44 @@
1
+ export interface ClipScoreResponse {
2
+ hook: number;
3
+ flow: number;
4
+ engagement: number;
5
+ trend: number;
6
+ why: string;
7
+ }
8
+ export interface ClipScore extends ClipScoreResponse {
9
+ score: number;
10
+ durationSec: number;
11
+ }
12
+ export interface ClipScoreWeights {
13
+ hook?: number;
14
+ flow?: number;
15
+ engagement?: number;
16
+ trend?: number;
17
+ }
18
+ /**
19
+ * Robust parser — tolerates prose around the JSON, missing keys, and
20
+ * out-of-range numbers. Mirrors `parseHookVisionResponse`.
21
+ */
22
+ export declare function parseClipScoreResponse(content: string): ClipScoreResponse;
23
+ /**
24
+ * Combine the four 0-1 dimensions into a single 0-100 score, using the
25
+ * default (or caller-supplied) weights.
26
+ */
27
+ export declare function combineClipScore(vision: ClipScoreResponse, durationSec: number, weights?: ClipScoreWeights): ClipScore;
28
+ export interface ScoreClipOptions {
29
+ apiKey?: string;
30
+ model?: string;
31
+ detail?: "low" | "high";
32
+ weights?: ClipScoreWeights;
33
+ signal?: AbortSignal;
34
+ }
35
+ /**
36
+ * Run the LLM scoring pass. Frames are optional — transcript-only is
37
+ * the cheap default; pass frames when visual energy matters.
38
+ *
39
+ * Pure-ish: the only side effects are the OpenAI fetch and reading
40
+ * frame files off disk. Used directly by both the tool wrapper and
41
+ * `find_viral_moments`.
42
+ */
43
+ export declare function scoreClipInternal(transcriptText: string, startSec: number, endSec: number, framePaths?: string[], opts?: ScoreClipOptions): Promise<ClipScore>;
44
+ //# sourceMappingURL=clip-scoring.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"clip-scoring.d.ts","sourceRoot":"","sources":["../../src/core/clip-scoring.ts"],"names":[],"mappings":"AAoBA,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;CACb;AAED,MAAM,WAAW,SAAU,SAAQ,iBAAiB;IAClD,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,gBAAgB;IAC/B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAyCD;;;GAGG;AACH,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,MAAM,GAAG,iBAAiB,CAczE;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAC9B,MAAM,EAAE,iBAAiB,EACzB,WAAW,EAAE,MAAM,EACnB,OAAO,GAAE,gBAAqB,GAC7B,SAAS,CAkBX;AAED,MAAM,WAAW,gBAAgB;IAC/B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,KAAK,GAAG,MAAM,CAAC;IACxB,OAAO,CAAC,EAAE,gBAAgB,CAAC;IAC3B,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB;AAED;;;;;;;GAOG;AACH,wBAAsB,iBAAiB,CACrC,cAAc,EAAE,MAAM,EACtB,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,MAAM,EACd,UAAU,GAAE,MAAM,EAAO,EACzB,IAAI,GAAE,gBAAqB,GAC1B,OAAO,CAAC,SAAS,CAAC,CAuDpB"}