@crievo/omni 0.0.0-stage → 0.1.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 (708) hide show
  1. package/.env.example +41 -0
  2. package/README.md +2 -2
  3. package/bin/omni.mjs +18 -0
  4. package/docs/onboarding/clone-voice.png +0 -0
  5. package/docs/onboarding/getting-started.png +0 -0
  6. package/docs/onboarding/new-video.png +0 -0
  7. package/docs/onboarding/providers.png +0 -0
  8. package/docs/onboarding/publish.png +0 -0
  9. package/docs/onboarding/references.png +0 -0
  10. package/docs/onboarding.md +212 -0
  11. package/omnivideo.config.example.json +437 -0
  12. package/package.json +40 -4
  13. package/reference/captions.md +156 -0
  14. package/reference/carousel-contract.md +91 -0
  15. package/reference/cloud.md +96 -0
  16. package/reference/learnings.md +792 -0
  17. package/reference/omnichat.md +305 -0
  18. package/reference/premiere-mcp.md +91 -0
  19. package/reference/publish.md +305 -0
  20. package/reference/scenes-contract.md +109 -0
  21. package/reference/studio.md +666 -0
  22. package/reference/voicestudio.md +81 -0
  23. package/reference/writing-for-voice.md +67 -0
  24. package/requirements.txt +3 -0
  25. package/studio/api/app.ts +87 -0
  26. package/studio/api/lib/errors.ts +33 -0
  27. package/studio/api/lib/files.ts +41 -0
  28. package/studio/api/lib/hono.ts +9 -0
  29. package/studio/api/lib/result.ts +14 -0
  30. package/studio/api/lib/validate.ts +31 -0
  31. package/studio/api/lib/video.ts +10 -0
  32. package/studio/api/middleware/json-charset.ts +11 -0
  33. package/studio/api/middleware/local-only.ts +32 -0
  34. package/studio/api/middleware/no-timeout.ts +10 -0
  35. package/studio/api/routes/admin.ts +18 -0
  36. package/studio/api/routes/agent-cascade.ts +100 -0
  37. package/studio/api/routes/agent-history.ts +23 -0
  38. package/studio/api/routes/agent.ts +162 -0
  39. package/studio/api/routes/analytics.ts +31 -0
  40. package/studio/api/routes/approval.ts +60 -0
  41. package/studio/api/routes/archive.ts +12 -0
  42. package/studio/api/routes/captions.ts +28 -0
  43. package/studio/api/routes/carousel.ts +38 -0
  44. package/studio/api/routes/clone.ts +27 -0
  45. package/studio/api/routes/create.ts +19 -0
  46. package/studio/api/routes/events.ts +11 -0
  47. package/studio/api/routes/export.ts +14 -0
  48. package/studio/api/routes/media.ts +30 -0
  49. package/studio/api/routes/omnichat.ts +113 -0
  50. package/studio/api/routes/onboarding.ts +14 -0
  51. package/studio/api/routes/publish.ts +40 -0
  52. package/studio/api/routes/queue.ts +14 -0
  53. package/studio/api/routes/references.ts +40 -0
  54. package/studio/api/routes/runner.ts +17 -0
  55. package/studio/api/routes/schedule.ts +47 -0
  56. package/studio/api/routes/services.ts +11 -0
  57. package/studio/api/routes/sessions.ts +27 -0
  58. package/studio/api/routes/settings.ts +102 -0
  59. package/studio/api/routes/sound.ts +45 -0
  60. package/studio/api/routes/thumbnail.ts +18 -0
  61. package/studio/api/routes/usage.ts +13 -0
  62. package/studio/api/routes/videos.ts +41 -0
  63. package/studio/api/routes/voices.ts +71 -0
  64. package/studio/api/services/admin.ts +113 -0
  65. package/studio/api/services/agent-cascade.ts +975 -0
  66. package/studio/api/services/agent-claude.ts +47 -0
  67. package/studio/api/services/agent-history.ts +103 -0
  68. package/studio/api/services/agent-persona.ts +77 -0
  69. package/studio/api/services/agent-tools.ts +2237 -0
  70. package/studio/api/services/agent-ui-tools.ts +259 -0
  71. package/studio/api/services/agent-wait.ts +32 -0
  72. package/studio/api/services/agent.ts +553 -0
  73. package/studio/api/services/analytics.ts +92 -0
  74. package/studio/api/services/approval.ts +134 -0
  75. package/studio/api/services/archive.ts +38 -0
  76. package/studio/api/services/captions.ts +268 -0
  77. package/studio/api/services/carousel.ts +206 -0
  78. package/studio/api/services/classify.ts +196 -0
  79. package/studio/api/services/clone-phrases.ts +75 -0
  80. package/studio/api/services/clone.ts +722 -0
  81. package/studio/api/services/create.ts +421 -0
  82. package/studio/api/services/events.ts +215 -0
  83. package/studio/api/services/media.ts +32 -0
  84. package/studio/api/services/omnichat.ts +624 -0
  85. package/studio/api/services/onboarding.ts +77 -0
  86. package/studio/api/services/publish.ts +287 -0
  87. package/studio/api/services/queue.ts +150 -0
  88. package/studio/api/services/references.ts +269 -0
  89. package/studio/api/services/root.ts +60 -0
  90. package/studio/api/services/services.ts +114 -0
  91. package/studio/api/services/sessions.ts +1007 -0
  92. package/studio/api/services/settings.ts +983 -0
  93. package/studio/api/services/sound.ts +311 -0
  94. package/studio/api/services/thumbnail.ts +133 -0
  95. package/studio/api/services/usage.ts +27 -0
  96. package/studio/api/services/videos.ts +418 -0
  97. package/studio/api/services/voice-pause.ts +98 -0
  98. package/studio/api/services/voices.ts +324 -0
  99. package/studio/runner/runner.ts +244 -0
  100. package/studio/shared/omni-actions.ts +68 -0
  101. package/studio/shared/omni-admin.ts +52 -0
  102. package/studio/shared/omni-farewell.ts +48 -0
  103. package/studio/shared/omni-hotword.ts +20 -0
  104. package/studio/shared/omni-screens.ts +228 -0
  105. package/studio/shared/schemas/admin.ts +15 -0
  106. package/studio/shared/schemas/agent-cascade.ts +58 -0
  107. package/studio/shared/schemas/agent-history.ts +73 -0
  108. package/studio/shared/schemas/agent.ts +50 -0
  109. package/studio/shared/schemas/analytics.ts +6 -0
  110. package/studio/shared/schemas/approval.ts +22 -0
  111. package/studio/shared/schemas/captions.ts +19 -0
  112. package/studio/shared/schemas/carousel.ts +26 -0
  113. package/studio/shared/schemas/clone.ts +40 -0
  114. package/studio/shared/schemas/common.ts +16 -0
  115. package/studio/shared/schemas/omnichat.ts +54 -0
  116. package/studio/shared/schemas/onboarding.ts +5 -0
  117. package/studio/shared/schemas/publish.ts +45 -0
  118. package/studio/shared/schemas/references.ts +20 -0
  119. package/studio/shared/schemas/settings.ts +82 -0
  120. package/studio/shared/schemas/sound.ts +22 -0
  121. package/studio/shared/schemas/videos.ts +38 -0
  122. package/studio/shared/schemas/voices.ts +23 -0
  123. package/studio/shared/types/api.ts +1290 -0
  124. package/studio/studio.ts +148 -0
  125. package/studio/tsconfig.json +15 -0
  126. package/studio/web/assets/logos/cartesia.svg +4 -0
  127. package/studio/web/assets/logos/elevenlabs.svg +12 -0
  128. package/studio/web/assets/logos/fish.svg +24 -0
  129. package/studio/web/assets/logos/omni-black.png +0 -0
  130. package/studio/web/assets/logos/omni-white.png +0 -0
  131. package/studio/web/assets/logos/voicestudio.svg +21 -0
  132. package/studio/web/components/AgentMark.tsx +7 -0
  133. package/studio/web/components/AgentWalking.tsx +11 -0
  134. package/studio/web/components/ApiKeyField.tsx +104 -0
  135. package/studio/web/components/AppShell.tsx +64 -0
  136. package/studio/web/components/ApprovalNotice.tsx +65 -0
  137. package/studio/web/components/AvatarGroupIcon.tsx +22 -0
  138. package/studio/web/components/AvatarNameField.tsx +86 -0
  139. package/studio/web/components/AvatarPicker.tsx +84 -0
  140. package/studio/web/components/BrandLogo.tsx +16 -0
  141. package/studio/web/components/CartesiaPlanSetting.tsx +90 -0
  142. package/studio/web/components/ClaudeWalking.tsx +32 -0
  143. package/studio/web/components/CloneVoiceDialog/GuidedScript.tsx +39 -0
  144. package/studio/web/components/CloneVoiceDialog/LanguageBar.tsx +37 -0
  145. package/studio/web/components/CloneVoiceDialog/LevelMeter.tsx +31 -0
  146. package/studio/web/components/CloneVoiceDialog/ProviderChoice.tsx +81 -0
  147. package/studio/web/components/CloneVoiceDialog/QualityPanel.tsx +62 -0
  148. package/studio/web/components/CloneVoiceDialog/RecordStep.tsx +50 -0
  149. package/studio/web/components/CloneVoiceDialog/RecorderBar.tsx +79 -0
  150. package/studio/web/components/CloneVoiceDialog/RedoSummary.tsx +54 -0
  151. package/studio/web/components/CloneVoiceDialog/SaveStep.tsx +91 -0
  152. package/studio/web/components/CloneVoiceDialog/SourceBar.tsx +30 -0
  153. package/studio/web/components/CloneVoiceDialog/index.tsx +141 -0
  154. package/studio/web/components/CloneVoiceDialog/types.ts +28 -0
  155. package/studio/web/components/CloneVoiceDialog/useCloneVoice.ts +528 -0
  156. package/studio/web/components/ConnectionSignals.tsx +51 -0
  157. package/studio/web/components/EditVoiceDialog/EditVoiceFooter.tsx +72 -0
  158. package/studio/web/components/EditVoiceDialog/ProviderCards.tsx +81 -0
  159. package/studio/web/components/EditVoiceDialog/index.tsx +256 -0
  160. package/studio/web/components/ErrorBanner.tsx +115 -0
  161. package/studio/web/components/FinishedTask.tsx +41 -0
  162. package/studio/web/components/FishModelSetting.tsx +69 -0
  163. package/studio/web/components/FishPriceSetting.tsx +44 -0
  164. package/studio/web/components/HumanBanner.tsx +64 -0
  165. package/studio/web/components/Lightbox.tsx +41 -0
  166. package/studio/web/components/LogStream.tsx +42 -0
  167. package/studio/web/components/Markdown.tsx +21 -0
  168. package/studio/web/components/MissingDmsPanel/BatchSend.tsx +137 -0
  169. package/studio/web/components/MissingDmsPanel/CommentCard.tsx +168 -0
  170. package/studio/web/components/MissingDmsPanel/CommentFeed.tsx +45 -0
  171. package/studio/web/components/MissingDmsPanel/DmBubble.tsx +38 -0
  172. package/studio/web/components/MissingDmsPanel/ReplyComposer.tsx +63 -0
  173. package/studio/web/components/MissingDmsPanel/index.tsx +98 -0
  174. package/studio/web/components/NotFound.tsx +48 -0
  175. package/studio/web/components/Omni/OmniAvatarPicker.tsx +92 -0
  176. package/studio/web/components/Omni/OmniCards.tsx +202 -0
  177. package/studio/web/components/Omni/OmniGlow.tsx +183 -0
  178. package/studio/web/components/Omni/OmniHistory.tsx +119 -0
  179. package/studio/web/components/Omni/OmniHotword.tsx +40 -0
  180. package/studio/web/components/Omni/OmniMicSelect.tsx +92 -0
  181. package/studio/web/components/Omni/OmniVoiceSample.tsx +81 -0
  182. package/studio/web/components/Omni/OmniWidget.tsx +743 -0
  183. package/studio/web/components/Omni/PendingActions.tsx +200 -0
  184. package/studio/web/components/Omni/Transcript.tsx +127 -0
  185. package/studio/web/components/OmnichatAccountStatus.tsx +87 -0
  186. package/studio/web/components/OmnichatRulePanel/RuleConfig.tsx +150 -0
  187. package/studio/web/components/OmnichatRulePanel/RuleDryRun.tsx +15 -0
  188. package/studio/web/components/OmnichatRulePanel/RuleEnableConfirm.tsx +42 -0
  189. package/studio/web/components/OmnichatRulePanel/RuleSkeleton.tsx +18 -0
  190. package/studio/web/components/OmnichatRulePanel/RuleStatus.tsx +55 -0
  191. package/studio/web/components/OmnichatRulePanel/index.tsx +115 -0
  192. package/studio/web/components/OmnichatRulePanel/rule-steps.ts +67 -0
  193. package/studio/web/components/OmnichatRulePanel/steps/MessageStep.tsx +91 -0
  194. package/studio/web/components/OmnichatRulePanel/steps/RepliesStep.tsx +201 -0
  195. package/studio/web/components/OmnichatRulePanel/steps/TriggerStep.tsx +76 -0
  196. package/studio/web/components/OmnichatRulePanel/steps/types.ts +11 -0
  197. package/studio/web/components/Palette.tsx +17 -0
  198. package/studio/web/components/PlatformCard.tsx +109 -0
  199. package/studio/web/components/PlatformIcon.tsx +19 -0
  200. package/studio/web/components/ProviderCard.tsx +80 -0
  201. package/studio/web/components/ProviderDetail.tsx +367 -0
  202. package/studio/web/components/ProviderLogo.tsx +20 -0
  203. package/studio/web/components/QueuePanel.tsx +77 -0
  204. package/studio/web/components/ReferenceActions.tsx +89 -0
  205. package/studio/web/components/ReferencePicker.tsx +136 -0
  206. package/studio/web/components/ResultMessage.tsx +7 -0
  207. package/studio/web/components/RuleForm.tsx +138 -0
  208. package/studio/web/components/RuleLog.tsx +151 -0
  209. package/studio/web/components/RunningTask.tsx +23 -0
  210. package/studio/web/components/SampleList.tsx +55 -0
  211. package/studio/web/components/SessionPanel.tsx +352 -0
  212. package/studio/web/components/SettingRow.tsx +14 -0
  213. package/studio/web/components/SettingsCategorySkeleton.tsx +25 -0
  214. package/studio/web/components/SidePanel.tsx +77 -0
  215. package/studio/web/components/Sidebar.tsx +289 -0
  216. package/studio/web/components/StageStepper.tsx +87 -0
  217. package/studio/web/components/StudioOffline.tsx +80 -0
  218. package/studio/web/components/TaskName.tsx +17 -0
  219. package/studio/web/components/TopBanners.tsx +30 -0
  220. package/studio/web/components/VideoOmnichatPanel.tsx +287 -0
  221. package/studio/web/components/VoiceAvatar.tsx +33 -0
  222. package/studio/web/components/VoiceCarousel.tsx +196 -0
  223. package/studio/web/components/VoiceCategory.tsx +9 -0
  224. package/studio/web/components/VoiceModal.tsx +39 -0
  225. package/studio/web/components/VoiceProfileCard.tsx +75 -0
  226. package/studio/web/components/VoiceProfilePicker.tsx +107 -0
  227. package/studio/web/components/VoiceProfilesSettings.tsx +150 -0
  228. package/studio/web/components/VoiceProviderCard.tsx +118 -0
  229. package/studio/web/components/VoiceProvidersCard.tsx +26 -0
  230. package/studio/web/components/WaitList.tsx +21 -0
  231. package/studio/web/components/icons.tsx +247 -0
  232. package/studio/web/components/ui/accordion.tsx +47 -0
  233. package/studio/web/components/ui/badge.tsx +18 -0
  234. package/studio/web/components/ui/calendar.tsx +43 -0
  235. package/studio/web/components/ui/chart.tsx +229 -0
  236. package/studio/web/components/ui/checkbox.tsx +26 -0
  237. package/studio/web/components/ui/cx.ts +2 -0
  238. package/studio/web/components/ui/date-time-picker.tsx +84 -0
  239. package/studio/web/components/ui/panel.tsx +23 -0
  240. package/studio/web/components/ui/picker.tsx +58 -0
  241. package/studio/web/components/ui/popover.tsx +24 -0
  242. package/studio/web/components/ui/select.tsx +89 -0
  243. package/studio/web/components/ui/skeleton.tsx +10 -0
  244. package/studio/web/components/ui/slider.tsx +68 -0
  245. package/studio/web/components/ui/switch.tsx +13 -0
  246. package/studio/web/components/ui/tooltip.tsx +59 -0
  247. package/studio/web/hooks/useAccounts.ts +27 -0
  248. package/studio/web/hooks/useAdmin.ts +12 -0
  249. package/studio/web/hooks/useAnalytics.ts +21 -0
  250. package/studio/web/hooks/useAvatarAudioReactive.ts +87 -0
  251. package/studio/web/hooks/useBrowserConnect.ts +48 -0
  252. package/studio/web/hooks/useClonePlan.ts +14 -0
  253. package/studio/web/hooks/useConnected.ts +7 -0
  254. package/studio/web/hooks/useLiveQuery.ts +29 -0
  255. package/studio/web/hooks/useMenuCollapsed.ts +32 -0
  256. package/studio/web/hooks/useMicDevices.ts +53 -0
  257. package/studio/web/hooks/useMissingDms.ts +211 -0
  258. package/studio/web/hooks/useMissingSetupSteps.ts +26 -0
  259. package/studio/web/hooks/useNow.ts +11 -0
  260. package/studio/web/hooks/useOmniAgent.ts +278 -0
  261. package/studio/web/hooks/useOmniClaude.ts +124 -0
  262. package/studio/web/hooks/useOmniDock.ts +142 -0
  263. package/studio/web/hooks/useOmniGreeting.ts +60 -0
  264. package/studio/web/hooks/useOmniHotword.ts +128 -0
  265. package/studio/web/hooks/useOmnichat.ts +26 -0
  266. package/studio/web/hooks/useOnboarding.ts +27 -0
  267. package/studio/web/hooks/usePost.ts +23 -0
  268. package/studio/web/hooks/useQueue.ts +10 -0
  269. package/studio/web/hooks/useReferences.ts +20 -0
  270. package/studio/web/hooks/useRuleEditor.ts +101 -0
  271. package/studio/web/hooks/useRunner.ts +24 -0
  272. package/studio/web/hooks/useSaveAction.ts +23 -0
  273. package/studio/web/hooks/useSchedule.ts +11 -0
  274. package/studio/web/hooks/useServices.ts +10 -0
  275. package/studio/web/hooks/useSessions.ts +15 -0
  276. package/studio/web/hooks/useSettings.ts +11 -0
  277. package/studio/web/hooks/useSettingsSection.ts +30 -0
  278. package/studio/web/hooks/useSidePanel.ts +23 -0
  279. package/studio/web/hooks/useSound.ts +30 -0
  280. package/studio/web/hooks/useUsage.ts +11 -0
  281. package/studio/web/hooks/useVideoVoice.ts +36 -0
  282. package/studio/web/hooks/useVideos.ts +21 -0
  283. package/studio/web/hooks/useVoiceProfilePreview.ts +52 -0
  284. package/studio/web/hooks/useVoiceProfiles.ts +7 -0
  285. package/studio/web/hooks/useVoiceRunning.ts +10 -0
  286. package/studio/web/hooks/useVoiceStudioClosed.ts +8 -0
  287. package/studio/web/index.html +19 -0
  288. package/studio/web/lib/agent.ts +7 -0
  289. package/studio/web/lib/alerts.ts +210 -0
  290. package/studio/web/lib/analytics-metrics.ts +351 -0
  291. package/studio/web/lib/api.ts +70 -0
  292. package/studio/web/lib/audio.ts +39 -0
  293. package/studio/web/lib/avatars.ts +41 -0
  294. package/studio/web/lib/calendar.ts +218 -0
  295. package/studio/web/lib/caption-position.ts +249 -0
  296. package/studio/web/lib/carousel.ts +24 -0
  297. package/studio/web/lib/chime.ts +42 -0
  298. package/studio/web/lib/clone-voice.ts +55 -0
  299. package/studio/web/lib/export.ts +11 -0
  300. package/studio/web/lib/files.ts +8 -0
  301. package/studio/web/lib/format.ts +93 -0
  302. package/studio/web/lib/media.ts +29 -0
  303. package/studio/web/lib/mic-device.ts +72 -0
  304. package/studio/web/lib/missing-dms.ts +70 -0
  305. package/studio/web/lib/omni-cards.ts +42 -0
  306. package/studio/web/lib/omni-cascade.ts +1067 -0
  307. package/studio/web/lib/omni-greeting-audio.ts +24 -0
  308. package/studio/web/lib/omni-hotword.ts +231 -0
  309. package/studio/web/lib/omni-panel.ts +67 -0
  310. package/studio/web/lib/omni-realtime.ts +811 -0
  311. package/studio/web/lib/omni-ui.ts +402 -0
  312. package/studio/web/lib/omni-wait.ts +174 -0
  313. package/studio/web/lib/omnichat-api.ts +44 -0
  314. package/studio/web/lib/orb.ts +270 -0
  315. package/studio/web/lib/pcm-recorder.ts +115 -0
  316. package/studio/web/lib/platforms.ts +17 -0
  317. package/studio/web/lib/providers.ts +27 -0
  318. package/studio/web/lib/publish-draft.ts +52 -0
  319. package/studio/web/lib/publish-settings.ts +81 -0
  320. package/studio/web/lib/query-client.ts +122 -0
  321. package/studio/web/lib/references.ts +13 -0
  322. package/studio/web/lib/services.ts +40 -0
  323. package/studio/web/lib/sessions.ts +103 -0
  324. package/studio/web/lib/settings-categories.ts +58 -0
  325. package/studio/web/lib/sound-audio.ts +33 -0
  326. package/studio/web/lib/sound-timeline.ts +291 -0
  327. package/studio/web/lib/sse.ts +86 -0
  328. package/studio/web/lib/stage-history.ts +141 -0
  329. package/studio/web/lib/stages.ts +344 -0
  330. package/studio/web/lib/studio-offline.ts +60 -0
  331. package/studio/web/lib/upload.ts +24 -0
  332. package/studio/web/lib/video-tabs.ts +118 -0
  333. package/studio/web/lib/voice-profile-draft.ts +34 -0
  334. package/studio/web/lib/voice-profiles.ts +3 -0
  335. package/studio/web/main.tsx +14 -0
  336. package/studio/web/pages/AdminPage/index.tsx +123 -0
  337. package/studio/web/pages/AgentPage/components/Measurements.tsx +102 -0
  338. package/studio/web/pages/AgentPage/index.tsx +28 -0
  339. package/studio/web/pages/AnalyticsPage/components/About.tsx +52 -0
  340. package/studio/web/pages/AnalyticsPage/components/ChannelCards.tsx +106 -0
  341. package/studio/web/pages/AnalyticsPage/components/ChannelView.tsx +137 -0
  342. package/studio/web/pages/AnalyticsPage/components/CollectionStatus.tsx +46 -0
  343. package/studio/web/pages/AnalyticsPage/components/Comparison.tsx +68 -0
  344. package/studio/web/pages/AnalyticsPage/components/Diagnosis.tsx +50 -0
  345. package/studio/web/pages/AnalyticsPage/components/Highlights.tsx +49 -0
  346. package/studio/web/pages/AnalyticsPage/components/Hint.tsx +11 -0
  347. package/studio/web/pages/AnalyticsPage/components/HookVsBody.tsx +100 -0
  348. package/studio/web/pages/AnalyticsPage/components/HourlyWave.tsx +133 -0
  349. package/studio/web/pages/AnalyticsPage/components/Insights.tsx +70 -0
  350. package/studio/web/pages/AnalyticsPage/components/MetricCard.tsx +46 -0
  351. package/studio/web/pages/AnalyticsPage/components/PlanSection.tsx +40 -0
  352. package/studio/web/pages/AnalyticsPage/components/ProgressionChart.tsx +155 -0
  353. package/studio/web/pages/AnalyticsPage/components/ReelBarsChart.tsx +154 -0
  354. package/studio/web/pages/AnalyticsPage/components/ReelThumb.tsx +19 -0
  355. package/studio/web/pages/AnalyticsPage/components/ReelView.tsx +290 -0
  356. package/studio/web/pages/AnalyticsPage/components/ReelsTable.tsx +269 -0
  357. package/studio/web/pages/AnalyticsPage/components/RequestWithFocus.tsx +38 -0
  358. package/studio/web/pages/AnalyticsPage/components/SegmentedControl.tsx +35 -0
  359. package/studio/web/pages/AnalyticsPage/components/VsMedian.tsx +31 -0
  360. package/studio/web/pages/AnalyticsPage/index.tsx +17 -0
  361. package/studio/web/pages/GettingStartedPage/index.tsx +174 -0
  362. package/studio/web/pages/NewVideoPage/components/DeliverablesPicker.tsx +62 -0
  363. package/studio/web/pages/NewVideoPage/components/FormatPicker.tsx +33 -0
  364. package/studio/web/pages/NewVideoPage/index.tsx +133 -0
  365. package/studio/web/pages/NotFoundPage/index.tsx +9 -0
  366. package/studio/web/pages/OmnichatPage/components/NewFromLink.tsx +59 -0
  367. package/studio/web/pages/OmnichatPage/components/RuleListItem.tsx +64 -0
  368. package/studio/web/pages/OmnichatPage/components/RulePreview.tsx +14 -0
  369. package/studio/web/pages/OmnichatPage/components/RuleView.tsx +88 -0
  370. package/studio/web/pages/OmnichatPage/index.tsx +153 -0
  371. package/studio/web/pages/QueuePage/index.tsx +22 -0
  372. package/studio/web/pages/ReferencePage/components/ReferenceTexts.tsx +131 -0
  373. package/studio/web/pages/ReferencePage/index.tsx +216 -0
  374. package/studio/web/pages/ReferencesPage/components/NewReferenceForm.tsx +150 -0
  375. package/studio/web/pages/ReferencesPage/components/ReferenceCard.tsx +42 -0
  376. package/studio/web/pages/ReferencesPage/components/ReferenceGridSkeleton.tsx +31 -0
  377. package/studio/web/pages/ReferencesPage/index.tsx +67 -0
  378. package/studio/web/pages/SchedulePage/components/CalendarSkeleton.tsx +18 -0
  379. package/studio/web/pages/SchedulePage/components/DayView.tsx +177 -0
  380. package/studio/web/pages/SchedulePage/components/HourPicker.tsx +103 -0
  381. package/studio/web/pages/SchedulePage/components/MonthDayCell.tsx +111 -0
  382. package/studio/web/pages/SchedulePage/components/MonthView.tsx +47 -0
  383. package/studio/web/pages/SchedulePage/components/PlatformIcons.tsx +12 -0
  384. package/studio/web/pages/SchedulePage/components/PostThumb.tsx +15 -0
  385. package/studio/web/pages/SchedulePage/components/WeekColumn.tsx +75 -0
  386. package/studio/web/pages/SchedulePage/components/WeekView.tsx +35 -0
  387. package/studio/web/pages/SchedulePage/index.tsx +285 -0
  388. package/studio/web/pages/SettingsPage/components/AgentAccounts.tsx +263 -0
  389. package/studio/web/pages/SettingsPage/components/AgentCategory.tsx +20 -0
  390. package/studio/web/pages/SettingsPage/components/ModelPicker.tsx +49 -0
  391. package/studio/web/pages/SettingsPage/components/ModelsSettings.tsx +138 -0
  392. package/studio/web/pages/SettingsPage/components/PlatformsCategory.tsx +23 -0
  393. package/studio/web/pages/SettingsPage/components/PublishingCategory/AccountStatus.tsx +29 -0
  394. package/studio/web/pages/SettingsPage/components/PublishingCategory/AutoPublishPanel.tsx +90 -0
  395. package/studio/web/pages/SettingsPage/components/PublishingCategory/BrowserConnect.tsx +62 -0
  396. package/studio/web/pages/SettingsPage/components/PublishingCategory/CheckField.tsx +19 -0
  397. package/studio/web/pages/SettingsPage/components/PublishingCategory/InstagramPanel.tsx +113 -0
  398. package/studio/web/pages/SettingsPage/components/PublishingCategory/OmnichatSettingsPanel.tsx +146 -0
  399. package/studio/web/pages/SettingsPage/components/PublishingCategory/SaveBar.tsx +33 -0
  400. package/studio/web/pages/SettingsPage/components/PublishingCategory/SchedulePanel.tsx +132 -0
  401. package/studio/web/pages/SettingsPage/components/PublishingCategory/SettingsCard.tsx +47 -0
  402. package/studio/web/pages/SettingsPage/components/PublishingCategory/SettingsCardGroup.tsx +19 -0
  403. package/studio/web/pages/SettingsPage/components/PublishingCategory/TextPanel.tsx +55 -0
  404. package/studio/web/pages/SettingsPage/components/PublishingCategory/TiktokPanel.tsx +123 -0
  405. package/studio/web/pages/SettingsPage/components/PublishingCategory/YoutubePanel.tsx +100 -0
  406. package/studio/web/pages/SettingsPage/components/PublishingCategory/badges.tsx +15 -0
  407. package/studio/web/pages/SettingsPage/components/PublishingCategory/index.tsx +40 -0
  408. package/studio/web/pages/SettingsPage/components/PublishingCategory/types.ts +10 -0
  409. package/studio/web/pages/SettingsPage/components/UsageCategory.tsx +389 -0
  410. package/studio/web/pages/SettingsPage/components/VoiceProvidersCategory.tsx +97 -0
  411. package/studio/web/pages/SettingsPage/index.tsx +85 -0
  412. package/studio/web/pages/VideoPage/components/AudioPlayer.tsx +113 -0
  413. package/studio/web/pages/VideoPage/components/CaptionPositionEditor/AxisField.tsx +50 -0
  414. package/studio/web/pages/VideoPage/components/CaptionPositionEditor/CaptionList.tsx +56 -0
  415. package/studio/web/pages/VideoPage/components/CaptionPositionEditor/CaptionListItem.tsx +92 -0
  416. package/studio/web/pages/VideoPage/components/CaptionPositionEditor/CaptionMargins.tsx +57 -0
  417. package/studio/web/pages/VideoPage/components/CaptionPositionEditor/CaptionStyleFields.tsx +70 -0
  418. package/studio/web/pages/VideoPage/components/CaptionPositionEditor/CaptionTextForm.tsx +87 -0
  419. package/studio/web/pages/VideoPage/components/CaptionPositionEditor/ColorField.tsx +37 -0
  420. package/studio/web/pages/VideoPage/components/CaptionPositionEditor/MarginField.tsx +59 -0
  421. package/studio/web/pages/VideoPage/components/CaptionPositionEditor/NumberField.tsx +57 -0
  422. package/studio/web/pages/VideoPage/components/CaptionPositionEditor/PositionControls.tsx +140 -0
  423. package/studio/web/pages/VideoPage/components/CaptionPositionEditor/captionEditor.ts +215 -0
  424. package/studio/web/pages/VideoPage/components/CaptionPositionEditor/useCaptionPosition.ts +154 -0
  425. package/studio/web/pages/VideoPage/components/CarouselChangeRequest.tsx +72 -0
  426. package/studio/web/pages/VideoPage/components/CarouselCompositionStage.tsx +80 -0
  427. package/studio/web/pages/VideoPage/components/CarouselCreate.tsx +63 -0
  428. package/studio/web/pages/VideoPage/components/CarouselMissing.tsx +19 -0
  429. package/studio/web/pages/VideoPage/components/CarouselPublishStage.tsx +173 -0
  430. package/studio/web/pages/VideoPage/components/CarouselScriptStage.tsx +82 -0
  431. package/studio/web/pages/VideoPage/components/CarouselSlideCard.tsx +49 -0
  432. package/studio/web/pages/VideoPage/components/CarouselViewer.tsx +55 -0
  433. package/studio/web/pages/VideoPage/components/ChangedVoices.tsx +53 -0
  434. package/studio/web/pages/VideoPage/components/CompositionStage.tsx +78 -0
  435. package/studio/web/pages/VideoPage/components/CoverPicker.tsx +32 -0
  436. package/studio/web/pages/VideoPage/components/EditorStage.tsx +53 -0
  437. package/studio/web/pages/VideoPage/components/LineTakes.tsx +160 -0
  438. package/studio/web/pages/VideoPage/components/LineTextEditor.tsx +124 -0
  439. package/studio/web/pages/VideoPage/components/LineVoicePicker.tsx +37 -0
  440. package/studio/web/pages/VideoPage/components/MediaImage.tsx +25 -0
  441. package/studio/web/pages/VideoPage/components/MotionScriptStage.tsx +39 -0
  442. package/studio/web/pages/VideoPage/components/NarrationStage.tsx +70 -0
  443. package/studio/web/pages/VideoPage/components/PublishForm.tsx +387 -0
  444. package/studio/web/pages/VideoPage/components/PublishStage.tsx +76 -0
  445. package/studio/web/pages/VideoPage/components/PublishText.tsx +149 -0
  446. package/studio/web/pages/VideoPage/components/ScenesApprovalPanel.tsx +278 -0
  447. package/studio/web/pages/VideoPage/components/ScheduledInfo.tsx +72 -0
  448. package/studio/web/pages/VideoPage/components/ScriptApprovalPanel.tsx +189 -0
  449. package/studio/web/pages/VideoPage/components/ScriptStage.tsx +17 -0
  450. package/studio/web/pages/VideoPage/components/SequencePlayer.tsx +146 -0
  451. package/studio/web/pages/VideoPage/components/SoundTimeline/AddSound.tsx +117 -0
  452. package/studio/web/pages/VideoPage/components/SoundTimeline/AreaSelection.tsx +74 -0
  453. package/studio/web/pages/VideoPage/components/SoundTimeline/BedPanel.tsx +94 -0
  454. package/studio/web/pages/VideoPage/components/SoundTimeline/BedRow.tsx +45 -0
  455. package/studio/web/pages/VideoPage/components/SoundTimeline/CaptionOverlay.tsx +44 -0
  456. package/studio/web/pages/VideoPage/components/SoundTimeline/CaptionSaveBar.tsx +32 -0
  457. package/studio/web/pages/VideoPage/components/SoundTimeline/CaptionsPanel.tsx +22 -0
  458. package/studio/web/pages/VideoPage/components/SoundTimeline/ClipBlock.tsx +111 -0
  459. package/studio/web/pages/VideoPage/components/SoundTimeline/ClipPanel.tsx +76 -0
  460. package/studio/web/pages/VideoPage/components/SoundTimeline/CutsPanel.tsx +44 -0
  461. package/studio/web/pages/VideoPage/components/SoundTimeline/EditorTabBar.tsx +73 -0
  462. package/studio/web/pages/VideoPage/components/SoundTimeline/EffectChoice.tsx +46 -0
  463. package/studio/web/pages/VideoPage/components/SoundTimeline/EffectItem.tsx +89 -0
  464. package/studio/web/pages/VideoPage/components/SoundTimeline/EffectPanel.tsx +232 -0
  465. package/studio/web/pages/VideoPage/components/SoundTimeline/EffectsRow.tsx +21 -0
  466. package/studio/web/pages/VideoPage/components/SoundTimeline/Lane.tsx +39 -0
  467. package/studio/web/pages/VideoPage/components/SoundTimeline/MarkerRow.tsx +40 -0
  468. package/studio/web/pages/VideoPage/components/SoundTimeline/MuteButton.tsx +36 -0
  469. package/studio/web/pages/VideoPage/components/SoundTimeline/NumberInput.tsx +37 -0
  470. package/studio/web/pages/VideoPage/components/SoundTimeline/PlanWarnings.tsx +19 -0
  471. package/studio/web/pages/VideoPage/components/SoundTimeline/PlayerResizeHandle.tsx +50 -0
  472. package/studio/web/pages/VideoPage/components/SoundTimeline/PreviewControls.tsx +51 -0
  473. package/studio/web/pages/VideoPage/components/SoundTimeline/SceneRow.tsx +54 -0
  474. package/studio/web/pages/VideoPage/components/SoundTimeline/SettingsPanel.tsx +65 -0
  475. package/studio/web/pages/VideoPage/components/SoundTimeline/SoundLibrary.tsx +99 -0
  476. package/studio/web/pages/VideoPage/components/SoundTimeline/SoundPreview.tsx +82 -0
  477. package/studio/web/pages/VideoPage/components/SoundTimeline/SpeedPanel.tsx +128 -0
  478. package/studio/web/pages/VideoPage/components/SoundTimeline/TimelineDock.tsx +56 -0
  479. package/studio/web/pages/VideoPage/components/SoundTimeline/TimelineFooter.tsx +26 -0
  480. package/studio/web/pages/VideoPage/components/SoundTimeline/TimelineSkeleton.tsx +21 -0
  481. package/studio/web/pages/VideoPage/components/SoundTimeline/TimelineToolbar.tsx +160 -0
  482. package/studio/web/pages/VideoPage/components/SoundTimeline/TimelineTracks.tsx +121 -0
  483. package/studio/web/pages/VideoPage/components/SoundTimeline/TrackSidebar.tsx +30 -0
  484. package/studio/web/pages/VideoPage/components/SoundTimeline/VoiceRow.tsx +53 -0
  485. package/studio/web/pages/VideoPage/components/SoundTimeline/Waveform.tsx +97 -0
  486. package/studio/web/pages/VideoPage/components/SoundTimeline/clipActions.ts +50 -0
  487. package/studio/web/pages/VideoPage/components/SoundTimeline/effectActions.ts +161 -0
  488. package/studio/web/pages/VideoPage/components/SoundTimeline/index.tsx +161 -0
  489. package/studio/web/pages/VideoPage/components/SoundTimeline/timelineGeometry.ts +45 -0
  490. package/studio/web/pages/VideoPage/components/SoundTimeline/types.ts +43 -0
  491. package/studio/web/pages/VideoPage/components/SoundTimeline/useAutosave.ts +107 -0
  492. package/studio/web/pages/VideoPage/components/SoundTimeline/useEditorShortcuts.ts +168 -0
  493. package/studio/web/pages/VideoPage/components/SoundTimeline/useFitHeight.ts +40 -0
  494. package/studio/web/pages/VideoPage/components/SoundTimeline/useFollowPlayhead.ts +21 -0
  495. package/studio/web/pages/VideoPage/components/SoundTimeline/useListenMix.ts +43 -0
  496. package/studio/web/pages/VideoPage/components/SoundTimeline/usePlayback.ts +231 -0
  497. package/studio/web/pages/VideoPage/components/SoundTimeline/usePlayerWidth.ts +27 -0
  498. package/studio/web/pages/VideoPage/components/SoundTimeline/useSoundData.ts +124 -0
  499. package/studio/web/pages/VideoPage/components/SoundTimeline/useSoundTimeline.ts +511 -0
  500. package/studio/web/pages/VideoPage/components/StageDrawer/CompareVersions.tsx +111 -0
  501. package/studio/web/pages/VideoPage/components/StageDrawer/VersionCard.tsx +81 -0
  502. package/studio/web/pages/VideoPage/components/StageDrawer/VersionMedia.tsx +24 -0
  503. package/studio/web/pages/VideoPage/components/StageDrawer/VersionText.tsx +15 -0
  504. package/studio/web/pages/VideoPage/components/StageDrawer/index.tsx +109 -0
  505. package/studio/web/pages/VideoPage/components/StageProcess.tsx +126 -0
  506. package/studio/web/pages/VideoPage/components/StageResult.tsx +69 -0
  507. package/studio/web/pages/VideoPage/components/SwapReference.tsx +92 -0
  508. package/studio/web/pages/VideoPage/components/ThumbnailPanel.tsx +252 -0
  509. package/studio/web/pages/VideoPage/components/TimingPanel.tsx +89 -0
  510. package/studio/web/pages/VideoPage/components/VideoPlayer.tsx +18 -0
  511. package/studio/web/pages/VideoPage/components/VideoSkeleton.tsx +50 -0
  512. package/studio/web/pages/VideoPage/components/VideoVoiceInfo.tsx +33 -0
  513. package/studio/web/pages/VideoPage/components/VideoVoicePicker.tsx +165 -0
  514. package/studio/web/pages/VideoPage/components/VoicePauseBanner.tsx +56 -0
  515. package/studio/web/pages/VideoPage/components/WithLogo.tsx +10 -0
  516. package/studio/web/pages/VideoPage/index.tsx +295 -0
  517. package/studio/web/pages/VideosPage/components/PublishedOn.tsx +85 -0
  518. package/studio/web/pages/VideosPage/components/ServicesPanel.tsx +81 -0
  519. package/studio/web/pages/VideosPage/components/SessionsPanel.tsx +62 -0
  520. package/studio/web/pages/VideosPage/components/VideoListSkeleton.tsx +25 -0
  521. package/studio/web/pages/VideosPage/components/VideoRow.tsx +122 -0
  522. package/studio/web/pages/VideosPage/index.tsx +134 -0
  523. package/studio/web/pages/VoicePage/index.tsx +28 -0
  524. package/studio/web/router.tsx +201 -0
  525. package/studio/web/styles/base/elements.css +55 -0
  526. package/studio/web/styles/base/utilities.css +25 -0
  527. package/studio/web/styles/components/badge.css +33 -0
  528. package/studio/web/styles/components/claude-running.css +27 -0
  529. package/studio/web/styles/components/claude-session.css +212 -0
  530. package/studio/web/styles/components/claude-walking.css +92 -0
  531. package/studio/web/styles/components/clone-voice-save.css +136 -0
  532. package/studio/web/styles/components/clone-voice.css +379 -0
  533. package/studio/web/styles/components/controls.css +70 -0
  534. package/studio/web/styles/components/data-and-image.css +54 -0
  535. package/studio/web/styles/components/forms.css +132 -0
  536. package/studio/web/styles/components/human-banner.css +99 -0
  537. package/studio/web/styles/components/lightbox.css +50 -0
  538. package/studio/web/styles/components/markdown.css +68 -0
  539. package/studio/web/styles/components/modal.css +70 -0
  540. package/studio/web/styles/components/omni-cards.css +105 -0
  541. package/studio/web/styles/components/omni.css +859 -0
  542. package/studio/web/styles/components/omnichat.css +538 -0
  543. package/studio/web/styles/components/panel.css +115 -0
  544. package/studio/web/styles/components/platform-card.css +140 -0
  545. package/studio/web/styles/components/provider-card.css +132 -0
  546. package/studio/web/styles/components/queue.css +185 -0
  547. package/studio/web/styles/components/reference-actions.css +133 -0
  548. package/studio/web/styles/components/reference-picker.css +146 -0
  549. package/studio/web/styles/components/results-drawer.css +224 -0
  550. package/studio/web/styles/components/segmented.css +115 -0
  551. package/studio/web/styles/components/side-panel-tab.css +23 -0
  552. package/studio/web/styles/components/status-signals.css +66 -0
  553. package/studio/web/styles/components/stepper.css +208 -0
  554. package/studio/web/styles/components/table.css +19 -0
  555. package/studio/web/styles/components/tabs.css +24 -0
  556. package/studio/web/styles/components/ui/accordion.css +71 -0
  557. package/studio/web/styles/components/ui/chart.css +162 -0
  558. package/studio/web/styles/components/ui/checkbox.css +37 -0
  559. package/studio/web/styles/components/ui/date-picker.css +153 -0
  560. package/studio/web/styles/components/ui/select.css +110 -0
  561. package/studio/web/styles/components/ui/skeleton.css +25 -0
  562. package/studio/web/styles/components/ui/slider.css +81 -0
  563. package/studio/web/styles/components/ui/switch.css +41 -0
  564. package/studio/web/styles/components/ui/tooltip.css +24 -0
  565. package/studio/web/styles/components/voice-carousel.css +255 -0
  566. package/studio/web/styles/components/voice-editor.css +179 -0
  567. package/studio/web/styles/components/voice-grid.css +56 -0
  568. package/studio/web/styles/components/voice-profiles.css +192 -0
  569. package/studio/web/styles/design-system.md +136 -0
  570. package/studio/web/styles/index.css +116 -0
  571. package/studio/web/styles/layout/page.css +41 -0
  572. package/studio/web/styles/layout/sidebar-groups.css +39 -0
  573. package/studio/web/styles/layout/sidebar.css +111 -0
  574. package/studio/web/styles/pages/agent.css +26 -0
  575. package/studio/web/styles/pages/analytics/dashboard.css +614 -0
  576. package/studio/web/styles/pages/analytics/insights.css +48 -0
  577. package/studio/web/styles/pages/getting-started.css +166 -0
  578. package/studio/web/styles/pages/new-video/form.css +32 -0
  579. package/studio/web/styles/pages/not-found.css +59 -0
  580. package/studio/web/styles/pages/references.css +204 -0
  581. package/studio/web/styles/pages/schedule/calendar.css +541 -0
  582. package/studio/web/styles/pages/schedule/chip-states.css +60 -0
  583. package/studio/web/styles/pages/settings/models.css +72 -0
  584. package/studio/web/styles/pages/settings/shell.css +361 -0
  585. package/studio/web/styles/pages/settings/usage.css +63 -0
  586. package/studio/web/styles/pages/studio-offline.css +105 -0
  587. package/studio/web/styles/pages/video/caption-controls.css +194 -0
  588. package/studio/web/styles/pages/video/captions-grid.css +70 -0
  589. package/studio/web/styles/pages/video/captions.css +79 -0
  590. package/studio/web/styles/pages/video/editor-chat.css +103 -0
  591. package/studio/web/styles/pages/video/export.css +50 -0
  592. package/studio/web/styles/pages/video/frames.css +29 -0
  593. package/studio/web/styles/pages/video/fullscreen-stage.css +400 -0
  594. package/studio/web/styles/pages/video/instagram-carousel.css +309 -0
  595. package/studio/web/styles/pages/video/layout.css +285 -0
  596. package/studio/web/styles/pages/video/narration.css +138 -0
  597. package/studio/web/styles/pages/video/preview-overview.css +11 -0
  598. package/studio/web/styles/pages/video/processing-notice.css +34 -0
  599. package/studio/web/styles/pages/video/publish-when.css +76 -0
  600. package/studio/web/styles/pages/video/publish.css +97 -0
  601. package/studio/web/styles/pages/video/result.css +96 -0
  602. package/studio/web/styles/pages/video/scenes.css +167 -0
  603. package/studio/web/styles/pages/video/sound-timeline.css +672 -0
  604. package/studio/web/styles/pages/video/sound-toolbar.css +149 -0
  605. package/studio/web/styles/pages/video/take-player.css +100 -0
  606. package/studio/web/styles/pages/video/thumbnail.css +157 -0
  607. package/studio/web/styles/pages/video/timeline.css +117 -0
  608. package/studio/web/styles/pages/video/voice-picker.css +14 -0
  609. package/studio/web/styles/pages/video-list/active-row.css +27 -0
  610. package/studio/web/styles/pages/video-list/card-link.css +49 -0
  611. package/studio/web/styles/pages/video-list/list.css +86 -0
  612. package/studio/web/styles/theme/identity/details.css +204 -0
  613. package/studio/web/styles/theme/identity/shell.css +249 -0
  614. package/studio/web/styles/theme/identity/surfaces.css +183 -0
  615. package/studio/web/styles/theme/identity/titles-and-buttons.css +120 -0
  616. package/studio/web/styles/theme/no-borders/blocks.css +208 -0
  617. package/studio/web/styles/theme/no-borders/controls.css +256 -0
  618. package/studio/web/styles/tokens.css +245 -0
  619. package/studio/web/types/api.ts +2 -0
  620. package/studio/web/types/assets.d.ts +9 -0
  621. package/templates/brief.md +27 -0
  622. package/templates/captions.html +204 -0
  623. package/templates/carousel-composition.md +47 -0
  624. package/templates/carousel-script.md +60 -0
  625. package/templates/components/voiceavatar/README.md +87 -0
  626. package/templates/components/voiceavatar/demo.html +50 -0
  627. package/templates/components/voiceavatar/voiceavatar.js +415 -0
  628. package/templates/motion.md +80 -0
  629. package/templates/playbook.md +43 -0
  630. package/templates/reference.md +123 -0
  631. package/templates/scene.html +112 -0
  632. package/templates/scenes.example.json +96 -0
  633. package/templates/script.md +63 -0
  634. package/templates/slide.html +71 -0
  635. package/templates/wireframe.html +91 -0
  636. package/tools/analysis.mjs +300 -0
  637. package/tools/approval.mjs +442 -0
  638. package/tools/assemble.mjs +942 -0
  639. package/tools/build-motion.mjs +437 -0
  640. package/tools/captions.mjs +246 -0
  641. package/tools/carousel.mjs +558 -0
  642. package/tools/compose.mjs +307 -0
  643. package/tools/cuts.mjs +74 -0
  644. package/tools/editor.mjs +419 -0
  645. package/tools/epidemic-capture.js +54 -0
  646. package/tools/fanout.mjs +69 -0
  647. package/tools/history.mjs +70 -0
  648. package/tools/history.py +61 -0
  649. package/tools/job-queue.mjs +180 -0
  650. package/tools/job_queue.py +430 -0
  651. package/tools/lib/agents.mjs +412 -0
  652. package/tools/lib/channel.mjs +399 -0
  653. package/tools/lib/claude-settings.mjs +67 -0
  654. package/tools/lib/cli.mjs +9 -0
  655. package/tools/lib/fanout.mjs +234 -0
  656. package/tools/lib/http.mjs +94 -0
  657. package/tools/lib/index.js +14 -0
  658. package/tools/lib/mcp-server.mjs +69 -0
  659. package/tools/lib/metrics/metrics.mjs +204 -0
  660. package/tools/lib/narration.mjs +15 -0
  661. package/tools/lib/oauth-client.mjs +295 -0
  662. package/tools/lib/omnichat/config.mjs +139 -0
  663. package/tools/lib/omnichat/errors.mjs +42 -0
  664. package/tools/lib/omnichat/extract.mjs +82 -0
  665. package/tools/lib/omnichat/gate.mjs +151 -0
  666. package/tools/lib/omnichat/manual.mjs +166 -0
  667. package/tools/lib/omnichat/match.mjs +78 -0
  668. package/tools/lib/omnichat/pass.mjs +393 -0
  669. package/tools/lib/omnichat/record.mjs +208 -0
  670. package/tools/lib/omnichat/text.mjs +315 -0
  671. package/tools/lib/omnichat/webhook.mjs +166 -0
  672. package/tools/lib/pairing.mjs +99 -0
  673. package/tools/lib/paths.mjs +41 -0
  674. package/tools/lib/platforms/instagram.mjs +469 -0
  675. package/tools/lib/platforms/tiktok.mjs +343 -0
  676. package/tools/lib/platforms/youtube.mjs +228 -0
  677. package/tools/lib/publish/publish.mjs +244 -0
  678. package/tools/lib/schedule/schedule.mjs +191 -0
  679. package/tools/lib/service.mjs +134 -0
  680. package/tools/lib/skill-client.mjs +126 -0
  681. package/tools/lib/skills-release.mjs +196 -0
  682. package/tools/lib/testing.mjs +52 -0
  683. package/tools/lib/up.mjs +187 -0
  684. package/tools/metrics.mjs +415 -0
  685. package/tools/models.mjs +134 -0
  686. package/tools/narrate.mjs +257 -0
  687. package/tools/omni.mjs +298 -0
  688. package/tools/omni_paths.py +12 -0
  689. package/tools/omnichat.mjs +1577 -0
  690. package/tools/omnivideo.py +1122 -0
  691. package/tools/package.mjs +130 -0
  692. package/tools/platforms.mjs +236 -0
  693. package/tools/premiere.mjs +533 -0
  694. package/tools/publish.mjs +1496 -0
  695. package/tools/reference.mjs +1408 -0
  696. package/tools/render.mjs +426 -0
  697. package/tools/runner.mjs +156 -0
  698. package/tools/setup.mjs +466 -0
  699. package/tools/skills-release.mjs +93 -0
  700. package/tools/sound.mjs +1323 -0
  701. package/tools/soundtrack.mjs +217 -0
  702. package/tools/thumbnail.mjs +271 -0
  703. package/tools/timing.mjs +108 -0
  704. package/tools/usage.py +628 -0
  705. package/tools/voice-envelope.mjs +185 -0
  706. package/tools/voice.py +448 -0
  707. package/tools/voice_sample.py +75 -0
  708. package/tools/wireframe.mjs +230 -0
@@ -0,0 +1,666 @@
1
+ # OmniVideo (studio)
2
+
3
+ The OmniVideo screen follows a design system taken from the ElevenLabs app: light
4
+ neutrals, Schibsted Grotesk for text and Geist Mono for numbers and code, from Google
5
+ Fonts. Tokens in `studio/web/styles/tokens.css`; the rest in the partials that
6
+ `studio/web/styles/index.css` imports, in cascade order. Details and where each rule
7
+ lives: `studio/web/styles/design-system.md`.
8
+
9
+ A local app that is the hub of this folder's pipeline. You can **start from
10
+ it**: a new video and a new reference become a Claude session (`claude -p`,
11
+ with the same skills as the chat), which the screen follows live and answers.
12
+ It also covers what the chat does poorly: seeing several videos at once,
13
+ reviewing media (players), coordinating the machine when videos run in
14
+ parallel, and browsing the reference library.
15
+
16
+ Each video's files remain the source of truth. The studio reads, watches and
17
+ calls the tools in `tools/`; it has no database and never redoes any tool's
18
+ work. Every tool works the same with the studio on or off, and the chat still
19
+ counts: a video touched from the chat shows up the same way.
20
+
21
+ ## Start
22
+
23
+ ```bash
24
+ cd <project folder>/studio && bun start # http://127.0.0.1:5432
25
+ bun start --open # and open it in the browser
26
+ bun run dev # with hot reload (for working on the studio)
27
+ bun studio.ts --videos <dir> # also show the videos from another folder
28
+ ```
29
+
30
+ `bun studio.ts --runner` (or `cloud.studio: true` in the config) also connects this server to the
31
+ hosted studio (`cloud/studio`, `reference/cloud.md`): the same app answers the requests that come
32
+ through the cloud, so the hosted screen is this one. It connects with the key that
33
+ `node tools/runner.mjs login` writes into `.env`, and picks a new one up within 30 s. `bun run build` writes the web the cloud serves
34
+ into `studio/dist/`.
35
+
36
+ `bun start` installs the dependencies (react, react-dom, marked) if missing
37
+ and starts the server. It listens only on `127.0.0.1:5432` (3900 and 3901
38
+ belong to VoiceStudio). Bun 1.4, TypeScript, React; no build step: Bun
39
+ bundles the web app at startup, and with `start` only at startup (changed the
40
+ web app, restart). `bun run dev` turns on Bun's development server (`--dev`):
41
+ a change in `web/` shows up in the browser right away (HMR, React and CSS
42
+ without reloading), and `--watch` restarts the server when `studio.ts` or
43
+ `server/` changes. Claude sessions and queue tools run in their own processes
44
+ and survive the restart. `bun run types` checks the types.
45
+
46
+ ## How the screen works
47
+
48
+ **Content, not just video.** Each folder in `videos/` is a piece of content that
49
+ delivers a video, an Instagram carousel or both (`content.json`, written by the
50
+ request). A new request picks it in the "O que criar" (What to create) cards;
51
+ carousel only hides format and voice. The content page has one stepper per
52
+ deliverable, with the Vídeo | Carrossel (Video | Carousel) switch on top when there
53
+ are two; the stages of each pipeline are in `web/lib/stages.ts` (`PIPELINES`) and
54
+ each one's result in `web/pages/VideoPage/components/StageResult.tsx` (the
55
+ carousel's in `web/pages/VideoPage/components/Carousel*.tsx`). A new pipeline = a
56
+ list in `PIPELINES`, its state in `getStageState` and one component per stage. The
57
+ carousel: **Roteiro** (Script: the slides, `script.md` and the caption, with "Pedir
58
+ alteração" (Request a change)), **Composição** (Composition: the slides in swipe
59
+ order, each with the build warnings and "Pedir alteração", the sheet and
60
+ `composition.md`) and **Publicar** (Publish: a one-slide-at-a-time viewer, the
61
+ editable caption, the button that uploads to Instagram, the JPEGs and the post's
62
+ omnichat). A video without a carousel gets "Fazer carrossel" (Make carousel) in the
63
+ header. The data comes from `api/services/carousel.ts`, which imports the diagnosis from
64
+ `tools/carousel.mjs`.
65
+
66
+ **The banners on top** (`web/components/TopBanners.tsx`, fixed on every screen; their joint height is
67
+ `--banner`): the studio not connected, in the hosted one (`RunnerBanner`), then **what broke** in red
68
+ (`ErrorBanner`, the rules in `web/lib/alerts.ts`), then **what waits for a decision** (`HumanBanner`:
69
+ script, scenes, voice being chosen). The red one lists a scheduled post that failed or missed its time, a
70
+ platform whose last publish failed (video or carousel), a video whose status can't be read, a studio
71
+ session that stopped with an error in the last 24 h, no agent account free (all at their limit, none
72
+ switched on or none logged in; one at its limit is the rotation working), a connected platform that
73
+ refuses its token or whose token expires within 7 days, and the omnichat paused by Meta or its cloud not
74
+ answering. Each line links to where it is fixed and leaves on its own when the condition clears; × hides
75
+ it in that browser until it fails again (the key names the occurrence). Settings, the platform accounts
76
+ and the omnichat state have no SSE notice, so the banner asks for them again every 3 min.
77
+
78
+ The video list has four tabs: **Em processamento** (In progress: pipeline
79
+ incomplete), **Finalizados** (Finished: exported, not published),
80
+ **Publicados** (Published: with a link on some platform, or scheduled; the
81
+ scheduled ones on top, with each platform's icon) and **Arquivados**
82
+ (Archived: the "Arquivar" (Archive) button at the end of the row;
83
+ "Desarquivar" (Unarchive) puts the video back in the group it would be in).
84
+ The classification is done by `web/lib/video-tabs.ts`.
85
+
86
+ The **Publicar** (Publish) stage also has the **Thumbnail (opcional)**
87
+ section, with four slots: "Gerar 3 aleatórias" (Generate 3 random) asks the
88
+ video's Claude for three randomly drawn ideas (v1–v3), and the fourth slot has
89
+ a box for your instruction (v4). The two requests are separate: redoing one
90
+ does not erase the other. All of them come out in the video's style and
91
+ aspect ratio, show up on screen by themselves, and each has "Usar este na
92
+ publicação" (Use this one in the post) and "Baixar" (Download) below it. The
93
+ selected one becomes `thumbnail/thumbnail.png` and is the post's cover: the
94
+ publish form shows which one goes, and `publish.mjs` sends it to YouTube and
95
+ Instagram at upload time (when scheduled, whichever is selected when the
96
+ scheduler runs goes). TikTok gets a frame from the video. "Selecionada ·
97
+ tirar" (Selected · remove) goes back to the video frame.
98
+
99
+ Every finished version has **"Pedir alteração"** (Request a change): the user
100
+ writes only what changes and Claude starts from that version's HTML, keeps
101
+ everything else literally (spot changes, no redesign) and renders only that
102
+ version. The previous HTML and PNG stay in `history/thumbnail/<vN>/`, the
103
+ request in `thumbnail/request.json` (`changes`), and "Alterada: …"
104
+ (Changed: …) appears under the version. Redoing the version from scratch
105
+ (drawing another 3, redoing yours) clears that mark; changing the selected one
106
+ removes the selection.
107
+
108
+ The screen is organized by the pipeline stages. Each video has a **stepper**
109
+ with the 11 stages of `status` (1, 2, P, 3…8), and each stage shows its
110
+ current state:
111
+
112
+ | state | when |
113
+ |---|---|
114
+ | feita (done) | `status` marks the stage |
115
+ | rodando · 1min12s (running) | a tool of this stage holds the turn, for this video |
116
+ | na fila · 2º (queued) | waiting for its turn (the position is in the whole machine's queue) |
117
+ | esperando você (waiting for you) | an approval round (script or scenes) waiting for the user |
118
+ | atenção (warning) | assembly outdated, or render with a changed start |
119
+ | erro (error) | the last run of this stage failed and it is not done |
120
+ | próxima (next) | the next stage in `status` |
121
+
122
+ | page | what |
123
+ |---|---|
124
+ | `/videos` | all videos at once, each with its stepper; clicking a stage opens the video on it. Alongside, the Claude sessions, the machine queue and the services |
125
+ | `/new` | new video: request, subject, format, duration, reference → folder, `source/request.md` and a Claude session |
126
+ | `/videos/<slug>[/<stage>]` | the stepper, the video's **Claude session** (below), and for the chosen stage: on the left the **process** (state, the running run with its live log or the last one with its log, who does it, command, what it writes); on the right the **result**; and the **"Resultados anteriores"** (Previous results) button, which opens the drawer |
127
+ | `/styles` | the reference library (cover, essence, measured summary, palette) and the request for a new one |
128
+ | `/voice` | **"Perfil de voz"** (Voice profile) in the menu (icon: two randomly drawn avatars): the voice carousel (profiles, active, edit, clone), the same as Configurações → Voz (Settings → Voice), straight from the menu |
129
+ | `/styles/<name>` | one reference: Claude session, palette, motion strip, videos, frames and sheets, guide and tokens in tabs, videos that use it |
130
+ | `/queue` | who is running (with a live log), who is waiting, the latest ones with each one's log |
131
+ | `/schedule[/week\|/day]` | the scheduled posts of every video (`schedule` in publication.json) by month, week and day, with each post's platforms and the selected cover. Only the day view shows the time, with the post at the exact time (posts at the same time share the width) and the now line; in the month view, hovering a day shows its videos with each one's time; in the week view, hovering a post shows its time. Clicking a post opens the video's Publicar stage; clicking a day (month) or the header (week) opens the day. **Dragging reschedules** (`POST /api/videos/<slug>/publish/schedule/reschedule`, `{ when }` → `publish.mjs schedule --reschedule`): in month and week, dropping on another day opens the time picker (starting at the post's current time); in the day view, the time is where the card lands, in 5 min steps, with a guide showing the time while dragging. A day or time already past does not accept the drop; a post being published does not drag |
132
+ | `/` and `/analytics/<slug>` | **Dashboard**, the first menu item and the home page, following shadcn's dashboard-01 pattern. At the top, the collection state ("coleta de 4 em 4 h · última leitura há…"; clicking turns the agent on and off), "Coletar agora" (Collect now; GET-only on the API) and "Gerar análise" (Generate analysis). Four metric cards: followers (change since the previous snapshot), views in 7 days (sum of the Reels published in the week, against the previous one), 3 s skip rate and % watched (medians of the last 5, against the 5 before; the "before OmniVideo" figure comes from the playbook). Progression: the hour-by-hour sum of the Reels (`progression` from the context) as a wave line with area, of views, comments, saves or followers, over 24 h, 7 or 30 days, with a dot at each published Reel. Comparing the Reels (y bar with metric tabs and the median; the ones checked in the table are highlighted) and Highlights (best hook, most saves and most comments per thousand views). Tabs: Reels (the data table: sort by column, Columns to show and hide, filter by pillar and goal, search, selection with "Comparar" (Compare) side by side with the median and the best of each row, pagination from 20), Insights (the summary sentence of the latest `channel/analyses/<day>.md` and each insight in an accordion; the full analysis and the request with a focus also in accordions; the Claude session, type `analysis`, target `channel`), Plano (Plan) and Gancho × miolo (Hook × body). Explanations live in ⓘ or in the footer accordions. `/analytics/<slug>`: one Reel, with the "Dashboard / title" breadcrumb, "Analisar este Reel" (Analyze this Reel), four cards against the channel median (views, 3 s skip rate, % watched, saves per thousand), the hour-by-hour wave line (`instagram.hourly`, 24 h and 72 h marked) and the tabs Diagnóstico (Diagnosis: the `analysis.md`, sections in accordions), Abertura (Opening: the sheet of the first 3 seconds and the script's hook), Marcos (Milestones) and Roteiro (Script); the other metrics in an accordion |
133
+
134
+ Without a stage in the URL, the page opens on what matters now: what is
135
+ running, what is waiting for you, what failed, what is queued, the next one; a
136
+ finished video opens on the export.
137
+
138
+ The result of each stage:
139
+
140
+ | stage | result |
141
+ |---|---|
142
+ | 1 script | `script.md` and `brief.md` |
143
+ | 2 motion script | table of the scenes (title, hero, goal) and `motion.md` |
144
+ | 3 composition | still and sheet of each scene with the minimum duration, the transition, the beats and how many sounds; the cut sheets, the overall ones |
145
+ | 4 narration | the lines of each scene and each beat's `enters_with` |
146
+ | 5 voice | each line with a player, duration and words per second |
147
+ | 5 timing | the video's timeline (scenes, lines and beats at the computed times) and the table |
148
+ | 6 render | each scene's MP4 and the **sound timeline** (editor below) |
149
+ | 7 captions | **position on screen** (editor below), and the captions from `captions.srt` |
150
+ | 8 export | the MP4 from `assemble.mjs`, what went in (scenes, lines, effects, captions, loudness) and the check sheet |
151
+
152
+ **Caption position** (stage 7): a preview of `templates/captions.html` (the
153
+ same HTML that `assemble.mjs` renders) over the scene's real frame, with the
154
+ safe area marked. You pick the scope (all, the scene of the selected caption,
155
+ only that caption), the platform's safe area and the style (only in all), the
156
+ anchor in the 3×3 grid and X/Y (slider, number, ±10, dragging the caption in
157
+ the preview). In All, also the safe area **Margens** (Margins: top, bottom,
158
+ left, right, in % of the frame; changing the platform resets the adjustment).
159
+ A caption may sit outside the safe area: the preview shows the warning with
160
+ the side and the px, and does not block. Inherited values appear faded. Below
161
+ the position, **Cor, contorno e sombra** (Color, outline and shadow: text,
162
+ pill background and opacity, outline, shadow with color, opacity, blur and
163
+ X/Y), in the same chosen scope (all, scene or only that caption); each field
164
+ has ↺ to go back to the level above or the style default. **"Gravar
165
+ posição"** (Save position) writes `captions` in `scenes.json`; it applies at
166
+ the next assembly. Rules in `reference/captions.md`.
167
+
168
+ **Sound timeline** (stage 6, full screen, without the result header): on the
169
+ left, the preview filling the space, with the controls in a floating pill in
170
+ the bottom corner (play, start, voice volume from 0 to 100% only for listening
171
+ here (the export keeps the voice at its own level; the effects' ducking stays
172
+ the same, so you hear the effects as they sit under the voice), time, zoom, ?
173
+ with the voice level and the tips), and the timeline below, with Renderizar
174
+ (Render) and the save state; both stay fixed, fitting the window. On the
175
+ right, the only column that scrolls: Resultados anteriores, the plan's
176
+ warnings, the chosen effect's panel (the column scrolls back to the top when
177
+ choosing), the **sound library** in 3-column cards (the ones captured for the
178
+ video and the kit by role, with search and ▶ to hear the file) and, after it,
179
+ the scene renders in 2 columns. The timeline has a ruler, scenes, lines (with
180
+ the ducking segments shaded), a diamond per beat (▲ at scene changes), the
181
+ effects in lanes colored by kit role, and the bed. Clicking an empty diamond
182
+ puts a sound on the beat (▲: on the scene change); dragging an effect changes
183
+ its `offset`, and with **Shift** duplicates it (the original stays; the copy,
184
+ with the same settings, goes to the beat nearest to where it was dropped);
185
+ **Ctrl or Cmd + click** selects several, which drag together and, with Shift,
186
+ duplicate together (each copy with the same duration and the same spacing, and
187
+ the copies stay selected); **Delete** or Backspace removes whatever is
188
+ selected (one, several or the bed). A library sound goes in by dragging it to
189
+ the timeline, or with one click on it and another on the timeline (Esc
190
+ cancels): on a diamond it becomes a layer of that beat, on an effect it
191
+ replaces that effect's sound, on the bed row it becomes the bed (music only
192
+ goes there), and on empty space it goes to the nearest beat, with the
193
+ `offset` that puts the peak where it was dropped. The chosen effect's panel
194
+ has the sound (the kit by role and what was captured for the video), anchor,
195
+ `offset`, volume, the **trim** on the file's waveform (drag the edges),
196
+ fades, stretch (`dur`) or speed, pitch, ducking, layers (+ layer) and remove.
197
+ The bed has sound, volume, from/to, fades, loop and ducking. Every adjustment
198
+ is planned by `tools/sound.mjs` itself in a draft (`.sound-draft/<slug>/`,
199
+ outside the video folder, so it doesn't trigger the watcher), and the screen
200
+ plays the finished WAVs with the voice in Web Audio, with the muted export on
201
+ top just for the picture: what you hear is what `assemble.mjs` mixes. Change
202
+ something while playing and it restarts from the same point; Space plays and
203
+ pauses. **Saving is automatic**: 5 s after the last edit, the draft goes to
204
+ `scenes.json` (`sound` on the beats, `transition.sound` and `bed`), and
205
+ leaving the screen before that saves on the way out. Saving does not render:
206
+ **Renderizar** (runs `assemble.mjs` through the queue) lights up when there is
207
+ a saved edit the export doesn't have yet (`scenes.json` newer than the
208
+ export MP4).
209
+
210
+ A stage's **previous results** drawer has the versions the tools kept (see
211
+ below), each with the queue run that produced it and its log, to play, view
212
+ and **compare side by side with the current one**; and every run of the stage
213
+ for this video, with its log.
214
+
215
+ Stage and next come from `omnivideo.py status --json`; each beat's time from
216
+ `tools/timing.mjs` (`beatTime`), the same one the build uses. Everything
217
+ updates by itself: the server watches the video folders and `.queue/` and
218
+ notifies the screen over SSE.
219
+
220
+ ## Previous versions (`history/`)
221
+
222
+ The tools overwrite their result on every run. So the drawer has something to
223
+ show, before overwriting they copy the version that was there, with the
224
+ file's original time, into the video:
225
+
226
+ ```
227
+ videos/<slug>/history/<stage>/[<key>/]<stamp>/
228
+ ```
229
+
230
+ | stage | who keeps it | what | key |
231
+ |---|---|---|---|
232
+ | `composition` | `build-motion.mjs --stills/--sheets/--transitions` | the scene's still and sheet; the cut's sheet | `c01`, `transition-c01-c02` |
233
+ | `voice` | `omnivideo.py voice` | the WAV and `voice/<line>.json` (the text it said) | `c01f1` |
234
+ | `render` | `build-motion.mjs --render` | the scene's MP4 and `render.json` | `c01` |
235
+ | `sound` | `sound.mjs` (and `assemble.mjs`) | `plan.json` and `preview.wav` | — |
236
+ | `captions` | `omnivideo.py captions` | `captions.srt` | — |
237
+ | `assembly` | `assemble.mjs` (and `premiere.mjs assemble`, if used) | the MP4, the sheet and `assembly.json` (or `premiere.json`) | — |
238
+ | `check` | `premiere.mjs check` (optional) | the frames | — |
239
+
240
+ - **Only what changed is kept.** If the new result comes out equal to the
241
+ previous one (deterministic render of a scene that didn't change, `sound`
242
+ with no change), the copy is discarded.
243
+ - **The last 5 are kept** for each result (`history.versions` in the config).
244
+ - **The version leads to the run by time.** The stamp is the time the
245
+ original result was written, which falls inside the run that produced it;
246
+ the screen finds that run in the queue. Composition doesn't go through the
247
+ queue, so its versions have no run.
248
+ - **Loose exports:** the stage 8 drawer also lists the other MP4s in
249
+ `export/` (an old Premiere export, for example).
250
+ - The helper is `tools/history.mjs` (`keepPrevious`) and its Python twin
251
+ `tools/history.py` (`keep_previous`). `history/` is outside git.
252
+ - Since this version, `voice` writes `voice/<line>.json` next to each WAV
253
+ (text, instruction, hash, duration), like the MP4's `render.json`: that is
254
+ what lets a line's previous version show what it said.
255
+
256
+ Services, without touching anything:
257
+
258
+ | service | how it is seen |
259
+ |---|---|
260
+ | VoiceStudio | `GET /health` |
261
+ | Premiere | the heartbeat the MCP Bridge panel writes to `<premiere.temp_dir>/bridge-heartbeat.json` every ~1 s. Up to 2.5 s the bridge is connected (the same limit as the MCP server); up to 15 s, slow bridge (the panel lags with Premiere busy); beyond that, bridge down |
262
+ | memory | `sysctl vm.swapusage kern.memorystatus_level kern.memorystatus_vm_pressure_level`; swap ≥ 90 % turns red: the voice starts blowing past the 305 s |
263
+
264
+ The studio does **not** call `premiere.mjs status`: `verify_premiere_connection`
265
+ opens Premiere if it is closed and sends a script to the bridge, which runs
266
+ one at a time, in the middle of an assembly.
267
+
268
+ ## Machine queue
269
+
270
+ One heavy task at a time on the machine, whether it comes from the chat, the
271
+ terminal or the studio:
272
+
273
+ | tool | goes through the queue | stays out |
274
+ |---|---|---|
275
+ | `omnivideo.py` | `voice` (only if there is a line to generate), `captions` | `status`, `timing` |
276
+ | `build-motion.mjs` | with `--render` | stills, sheets and transitions |
277
+ | `sound.mjs` | `sound.mjs videos/<slug>` | `capture`, `download`, `effects`; the `prepareSound` inside `assemble` is already in its turn |
278
+ | `assemble.mjs` | assembling and exporting, and `--sheet` (tool `assembly`) | — |
279
+ | `premiere.mjs` (optional) | `assemble`, `check`, `export` | `status`, `call` |
280
+
281
+ `check` goes in because the Premiere bridge runs one script at a time and
282
+ `export` locks Premiere: a `check` in the middle of an export would get no
283
+ answer. For the same reason, `premiere.mjs status` with `assemble`, `check`
284
+ or `export` running answers "Premiere ocupado" (Premiere busy) right away
285
+ (exit 0), without talking to the bridge, and `call` refuses (exit 1) until the
286
+ task ends.
287
+
288
+ Whatever can be checked without the machine is checked **before** waiting for
289
+ the turn: `voice` with no new line, `captions` without audio or without
290
+ timing, `--render` with provisional timing, `assemble` with a missing MP4 or
291
+ captions, `check` or `export` without `premiere.json`. The error comes out
292
+ right away; a chat doesn't wait half an hour in the queue to find out a file
293
+ was missing.
294
+
295
+ ### How it works
296
+
297
+ The mechanism is always the same, with the studio on or not: **the file lock
298
+ is the queue, and the studio only shows it.** So nothing changes when the
299
+ studio crashes, restarts or starts in the middle of a task.
300
+
301
+ 1. The tool writes a ticket, `.queue/<id>.json`: tool, video, command, pid,
302
+ origin (`chat`, `terminal`, `studio`), arrival.
303
+ 2. It waits to be the oldest live ticket waiting and then takes the `flock`
304
+ on `.queue/machine.lock`. The kernel releases the flock when the process
305
+ dies, even with `kill -9`: there is no forgotten lock.
306
+ 3. On its turn, the tool's output goes to the terminal as always **and** to
307
+ `.queue/<id>.log`, which the screen shows live.
308
+ 4. On exit, the ticket becomes `ok`, `error` (with the exit code) or
309
+ `interrupted`.
310
+
311
+ A `waiting` or `running` ticket of a dead process, or without a heartbeat for
312
+ more than 15 s (a live ticket renews its mtime every 2 s; that is what catches
313
+ a reused pid), counts as interrupted and gets out of the way. The last 100
314
+ finished tickets are kept, with their logs.
315
+
316
+ The protocol lives in `tools/job_queue.py` (`with turn("voice", dir):`). The
317
+ Node tools use `tools/job-queue.mjs` (`await turn("render", dir)`), which
318
+ runs `job_queue.py` as a guardian: a Python process that waits for the turn,
319
+ holds the flock while the Node process lives and releases it when the stdin
320
+ pipe closes.
321
+
322
+ Details:
323
+
324
+ - **Without contention, nothing changes in the output.** The waiting notice
325
+ only appears when there is a wait.
326
+ - **Whoever waits re-reads `scenes.json` on its turn.** The wait can be long
327
+ and the chat may have changed the video meanwhile.
328
+ - **A child inside its parent's turn doesn't wait.** The task holding the turn
329
+ passes `OMNIVIDEO_TURN` to its children; a child asking for the turn inside
330
+ it goes straight through (avoids the deadlock of a heavy tool calling
331
+ another).
332
+ - **`OMNIVIDEO_QUEUE=0` turns the queue off.** Emergencies only: without it,
333
+ two heavy tasks fight over the machine again.
334
+
335
+ ### In the terminal
336
+
337
+ ```bash
338
+ PY=.venv/bin/python
339
+ $PY tools/job_queue.py # who runs, who waits, the latest ones
340
+ $PY tools/job_queue.py --json # the same as data
341
+ ```
342
+
343
+ Whoever waits sees on stderr (the message text is PT):
344
+
345
+ ```
346
+ fila: esperando a vez de voz · rodando voz · 2026-10-12-x (chat 04e33526, pid 86225) há 2min13s; 1 na frente
347
+ fila: vez de voz depois de 2min30s de espera
348
+ ```
349
+
350
+ ## The chat and the queue
351
+
352
+ - **A heavy tool may wait for its turn.** With another video running, a
353
+ `voice` or a `--render` can sit for minutes before starting. Run heavy tools
354
+ in the background (`run_in_background`) and wait for the completion
355
+ notification, instead of a command with a timeout: the Bash timeout kills
356
+ the tool mid-wait, and it loses its place in the queue.
357
+ - **Before concluding it is stuck,** look at `$PY tools/job_queue.py`: if it is
358
+ `waiting`, it is in the queue; if it is `running`, the log is in
359
+ `.queue/<id>.log`.
360
+ - **Don't kill another video's task** to jump ahead. It belongs to another
361
+ session.
362
+
363
+ ## Scene approval in the studio
364
+
365
+ After the motion script, `av-motion` opens an approval round
366
+ (`tools/approval.mjs open`) and the session sits in `approval.mjs wait`. In
367
+ stage 2 of the video page, each scene shows title, hero, goal and visual,
368
+ with **"Aprovar"** (Approve) and **"Pedir alteração"** (Request a change; the
369
+ comment is required: it is what Claude will adjust), and **"Aprovar todas"**
370
+ (Approve all) at the bottom.
371
+
372
+ - **Notice.** On any studio page, a new round plays a short sound (Web Audio,
373
+ synthesized; the browser only plays it after some click on the page) and the
374
+ tab title gets `● Aprovar cenas 9:41 · …` with the countdown. A round that
375
+ was already waiting when the page opened only shows in the title.
376
+ - **Automatic approval.** With nobody touching it, the pending scenes approve
377
+ themselves at the deadline (`motion.approval.auto_approve_min`, 10 min) and
378
+ the session moves on to the composition. Whoever reads first after the deadline
379
+ applies it: `wait`, the studio or `status`.
380
+ - **Editing pauses the countdown.** Clicking Pedir alteração (or typing in the
381
+ comment) calls `scenes/pause`: what remains goes to `remaining_ms` and the
382
+ title shows `⏸ 7:32`, stopped. Sending or cancelling the last open change
383
+ calls `scenes/resume`, and the countdown picks up where it stopped
384
+ (`deadline = now + remaining_ms`). A screen that didn't pause (another tab,
385
+ reloaded page) shows **"Retomar contagem"** (Resume countdown). Approving a
386
+ scene pauses nothing.
387
+ - **Who waits.** The session stays in `wait` during the pause. Only if the
388
+ pause runs past the whole deadline (abandoned edit) does it exit with 4 and
389
+ the session stops. Then the studio handles the rest: a per-video alarm
390
+ applies the deadline when the countdown resumes and expires, and resumes the
391
+ stopped session (only for a round finished less than 1 h ago, so as not to
392
+ wake an old session).
393
+ - **End of the round.** With all decided, a session still in `wait` moves on
394
+ by itself; one that stopped is resumed by the studio (also when the decision
395
+ finishes while the turn is closing). With changes, `av-motion` adjusts only
396
+ those scenes and opens another round, with a new deadline; approved scenes
397
+ stay approved.
398
+
399
+ `motion.approval.enabled: false` in the config turns it off: the round is born
400
+ approved. The file, `motion/approval.json`, is described in the header of
401
+ `tools/approval.mjs`; in the chat, the user answers and Claude runs
402
+ `approval.mjs decide`.
403
+
404
+ ## Swapping the reference mid-video
405
+
406
+ In stage 2 (or by clicking "Estilo visual: …" at the top of the video), **"Trocar
407
+ estilo visual"** (Swap visual style) opens the same picker as the new video, with
408
+ the current one marked, and an optional comment. The request goes to the
409
+ video's session (resumes the stopped one or opens one), which follows the
410
+ `omnivideo` skill, the "visual style" row: new style in `motion.md`,
411
+ scenes reopened only if the visual changed, and then the composition (base and
412
+ scenes), render and export.
413
+
414
+ The state changes right away, without waiting for Claude: the studio writes
415
+ the request to `motion/reference-swap.json`. Stage 2 shows **refazendo**
416
+ (redoing) until `motion.md` is rewritten with the new reference. Script,
417
+ lines and voice stay. The screen says what will be redone depending on where
418
+ the video is. With the session running, the request is refused: wait for the
419
+ turn or stop the session.
420
+
421
+ ## Claude sessions
422
+
423
+ A request made on the screen becomes a `claude -p` session at the omnivideo
424
+ root, with the project's skills and CLAUDE.md. Each turn runs until Claude
425
+ stops (at an abandoned approval, at a blocker, at delivery); the next reply resumes
426
+ **the same session** (`--resume`), with everything it already knows.
427
+
428
+ | starts | prompt | goes until |
429
+ |---|---|---|
430
+ | New video (`/new`) | `/omnivideo` + the folder already created + `source/request.md` | delivery (it waits at the approvals, which resume it) |
431
+ | Continue with Claude (video without a session) | `/omnivideo` resuming from where `status` says | the next stop |
432
+ | New reference | skill `av-reference`: measure, Tipo, curves, GUIDE.md, proof in code compared with the piece | guide written and proof |
433
+ | Ask Claude (reference) | the request, with the reference folder | what was asked |
434
+ | Reply | the text, in the same session | the next stop |
435
+
436
+ **Scenes decided in the studio resume the session** that stopped at scene
437
+ approval (section Scene approval in the studio).
438
+
439
+ **One running session per video or reference.** Replying mid-turn is refused;
440
+ the **"Parar"** (Stop) button ends the whole process group (claude and the
441
+ tools it opened). Whatever was already written stays; "continuar" (continue)
442
+ resumes.
443
+
444
+ **Where it lives.** `.sessions/<id>/session.json` (type, target, turns with
445
+ prompt and pid) and `turn-<n>.jsonl` (each turn's stream-json output). The
446
+ state comes from there: a turn with a `result` finished (stopped or error),
447
+ without `result` and with a live pid it is running, without both it was
448
+ interrupted. claude runs in its own process group and writes straight to the
449
+ file: the studio can go down and come up mid-turn. To open it in the
450
+ terminal: `claude --resume <id>`.
451
+
452
+ **Permissions.** With no terminal, nobody approves anything: whatever asks for
453
+ approval is denied immediately (`--permission-prompts none`). `auto` mode
454
+ denies almost everything in that situation; that's why it is `acceptEdits`
455
+ plus a list in `studio.claude.allow` (config), with `{python}` replaced by
456
+ VoiceStudio's Python. A Bash rule doesn't match a shell variable, so the
457
+ session gets a system prompt asking for the full Python path, one command per
458
+ call and heavy tools in the foreground (the Bash timeout goes up to
459
+ `studio.claude.bash_timeout_ms`). Whatever is denied shows on screen in red;
460
+ add the rule to the config and reply asking it to try again.
461
+
462
+ | `studio.claude` | |
463
+ |---|---|
464
+ | `bin` | the executable (`claude`) |
465
+ | account | not a field here: `agents.accounts` (below) |
466
+ | the model | the session's model and effort come from `models.orchestrator` (video) and `models.reference` in the config, re-read every turn (`--model`, `--effort`). The stages run in a fork with the model from `models.stages`, written into the skills by `tools/models.mjs apply` |
467
+ | `permission_mode` | `auto` (the classifier approves the common and denies the risky; re-read every turn). `acceptEdits` goes back to depending only on the allowlist |
468
+ | `budget_usd` | ceiling per turn (`--max-budget-usd`), `null` for no ceiling |
469
+ | `bash_timeout_ms` | the limit of one command: 2 h, for long voice and render runs |
470
+ | `allow` | allowed tools and Bash rules |
471
+
472
+ ### Agent accounts
473
+
474
+ `agents` in the config, the logic in `tools/lib/agents.mjs` (shared with `publish.mjs text` and the
475
+ omnichat classifier), the screen in Configurações → Agente de IA → Contas de agente.
476
+
477
+ | `agents` | |
478
+ |---|---|
479
+ | `accounts` | `[{ id, provider: "claude" \| "codex", dir, label?, enabled? }]`, in rotation order. `dir` is the account's `CLAUDE_CONFIG_DIR` or `CODEX_HOME`, each with its own login (`claude auth login` / `codex login` with the variable set; the screen's "Entrar" runs it). Empty: `~/.claude-personal`. `~/.claude` is refused in code |
480
+ | `codex.model`, `codex.effort` | the model of every Codex session (`-m`, `model_reasoning_effort`); the options come from the account's `models_cache.json` |
481
+ | `codex.sandbox` | `auto` (default): workspace-write with network and the library writable, `approval_policy=on-request` with Codex's `auto_review` reviewer, so what must leave the sandbox (Chrome in the renders) asks and is decided like Claude's auto mode. `full`: no sandbox |
482
+ | `codex.bin` | the CLI; default `codex` on the PATH or the one the ChatGPT app ships |
483
+
484
+ - **Rotation:** a new session goes to the next ready account (on, logged in, not over its limit);
485
+ `.accounts/state.json` keeps the cursor and the limits. A session stays on its account while it
486
+ is free.
487
+ - **Limit:** a turn that ends on the account's limit (Claude: the 429 with the rejected
488
+ `rate_limit_event` and its `resetsAt`; Codex: `turn.failed` with the usage-limit message) sets the
489
+ account aside until the reset (1 h when unknown) and starts a `failover` turn on the next one, at
490
+ most once per account. Claude to Claude copies the transcript (`<dir>/projects/<cwd>/<id>.jsonl`
491
+ and its subagents) and resumes the same conversation; to or from Codex the turn opens a new
492
+ conversation whose prompt carries the first request, the last turns and the instruction to check
493
+ the files before acting (`handoff` in the turn). With no account free, the turn stays on the
494
+ limit message and "Liberar" on the screen puts an account back by hand.
495
+ - **Codex turns:** `codex exec --json` (then `exec resume <thread>`), the thread id read from the
496
+ turn's first event, `CLAUDE.md` as the project doc (`project_doc_fallback_filenames`), the
497
+ studio's context plus the Codex notes as `developer_instructions`, and the skills through
498
+ `.agents/skills`. It reports tokens, not dollars.
499
+
500
+ ## References
501
+
502
+ The library (`references.dir` in the config) is each video's design system:
503
+ the motion script picks a reference and takes from it the palette,
504
+ typography, curves and rhythm of every scene. On the screen, each reference
505
+ shows what exists in its folder: guide (`GUIDE.md` or the first `.md`),
506
+ `measurements.json` (summary, palette by area, motion strip), tokens
507
+ (`*tokens*.json`, where the palette comes from when there are no
508
+ measurements), videos, `frames/` and `sheets/`. **"Novo estilo visual"** (New
509
+ visual style) asks for a link and/or photos (nothing else: the name comes from
510
+ the link or the photos) and opens a session that measures and writes the guide. Each reference's toggle adds or removes a
511
+ `.disabled` in the folder: disabled, it is faded and listed separately,
512
+ disappears from the new video picker, and `reference.mjs list` only mentions
513
+ it at the end, as "não use" (don't use). Deleting moves the folder to the
514
+ Trash.
515
+
516
+ ## Settings
517
+
518
+ `/settings`, by category. **Voz** (Voice): just the voice carousel (profiles
519
+ in `voice.profiles`), in the ElevenLabs style: the voice in the center becomes
520
+ the **active** one (`voice.default_profile`, the one preselected for a new
521
+ video) or opens for **editing**; the last item adds a voice. Adding or editing
522
+ opens a drawer: the voice's name and its **providers** (Fish Audio, Cartesia,
523
+ VoiceStudio). Each provider is where you enter what it needs: API key (in
524
+ `.env`, masked), Fish's model and fallback, Voice ID (or the VoiceStudio
525
+ profile, by name) and, on a new voice, recording or uploading audio to clone.
526
+ With more than one provider on the voice, the list order is the priority of
527
+ use (the profile's `order`): the 1st is used and the following ones are
528
+ fallbacks. API key and model apply to every voice of the provider and save
529
+ right away; Voice ID and order save on "Gravar voz" (Save voice). The avatar
530
+ is a liquid orb whose colors come from the voice's id; every avatar on the page is drawn by
531
+ `web/lib/orb.ts` (one shared WebGL context, the same shader as the video scenes'
532
+ `VoiceAvatar` component) and reacts to the voice while it plays. **Agente de IA:** the agent accounts (Claude
533
+ and Codex, in rotation order, each with its login, its limit and "Entrar"), Codex's model and
534
+ sandbox, and the Claude models per stage. The keys go
535
+ to `.env` and come back to the screen only masked; the rest goes to
536
+ `omnivideo.config.json`. The Uso (Usage) screen only shows what is chosen.
537
+
538
+ ## API
539
+
540
+ Everything on `127.0.0.1:5432`. A request with another `Host` is refused (DNS
541
+ rebinding); POST requires `content-type: application/json` and, if `Origin`
542
+ comes, that it be the studio's own.
543
+
544
+ | route | what |
545
+ |---|---|
546
+ | `GET /api/videos` | a summary per video: `status` (the JSON from `status --json`), the scenes' `approval` (with the expired deadline already applied), cover |
547
+ | `POST /api/videos` | `{ request, deliverables?, subject?, format?, duration?, reference? }` → folder, `content.json`, `source/request.md` and session; returns `slug` and `session`. `deliverables`: `["video"]` (default), `["carousel"]` or both |
548
+ | `POST /api/videos/<slug>/carousel/create` | `{ note? }` → puts the carousel in the deliverables and asks the content's session (from the folder's video, if any) |
549
+ | `POST /api/videos/<slug>/carousel/adjust` | `{ slide?, request, stage }` → a change asked of the session (`av-carousel … ajustar:`) |
550
+ | `POST /api/videos/<slug>/carousel/caption` | `{ caption }` → `carousel/publication.json` |
551
+ | `POST /api/videos/<slug>/carousel/publish` | `{ caption?, republish? }` → `publish.mjs carousel --confirm` (minutes) |
552
+ | `POST /api/videos/<slug>/reference` | `{ reference, note? }` → a request to the video's session to swap the style reference (`""` = Claude picks another); refuses a video without a motion script and the same reference without a note |
553
+ | `POST /api/videos/<slug>/claude` | `{ message }` → resumes the video's latest session, or opens one |
554
+ | `GET /api/sessions` | the sessions, most recent first: state, current turn, what Claude is doing now, final answer, denied permissions, cost |
555
+ | `GET /api/sessions/<id>` | the session with all its turns |
556
+ | `GET /api/sessions/<id>/log` | SSE: the session's readable transcript (`log`) and `end` when the turn stops |
557
+ | `POST /api/sessions/<id>/reply` | `{ message }` → new turn in the same session |
558
+ | `POST /api/sessions/<id>/stop` | ends the running turn |
559
+ | `GET /api/references` · `/api/references/<name>` | the library; one reference with texts, media and where it is used |
560
+ | `POST /api/references` | `{ name, source, note? }` → session that measures and writes the guide |
561
+ | `POST /api/references/<name>/claude` | `{ message }` → a request about a reference |
562
+ | `POST /api/references/<name>/enable` | `{ enabled }` → removes or adds the `.disabled` |
563
+ | `POST /api/references/<name>/delete` | moves the reference folder to the Trash |
564
+ | `GET /ref/<name>/<path>` | a file of a reference (image, video, text) |
565
+ | `GET /api/videos/<slug>` | everything about the video: `scenes.json`, resolved times, texts (scripts and `captions.srt`), media files with mtime, `history` (the kept versions), `runs` (every queue run of this video), `premiere.json`, `sound/plan.json` |
566
+ | `POST /api/videos/<slug>/scenes/decide` | `{ scene, decision: "approve" \| "change", comment? }` → `motion/approval.json`; change requires a comment. A finished round resumes the video's stopped session |
567
+ | `POST /api/videos/<slug>/scenes/approve-all` | the round's pending scenes become approved |
568
+ | `POST /api/videos/<slug>/scenes/pause` | the user opened a change: `mode: "paused"`, what remains goes to `remaining_ms` |
569
+ | `POST /api/videos/<slug>/scenes/resume` | the change was sent or cancelled: the countdown picks up where it stopped |
570
+ | `POST /api/videos/<slug>/voice/regenerate` | `{ line }` → `omnivideo.py voice --regenerate <line>`: another reading of the same text, through the queue, **of that line only** (even with the video's provider changed, the others are not redone); answers right away (only an error from before the queue comes back) |
571
+ | `POST /api/videos/<slug>/voice/generate` | `{}` → `omnivideo.py voice` with no arguments: generates the lines that are missing, changed text or were requested from another provider. It is the “Gerar as N falas com <voz>” (Generate the N lines with <voice>) button of the Voz stage picker, which only appears when the chosen provider differs from that of some already generated line |
572
+ | `POST /api/videos/<slug>/voice/use` | `{ line, version }` → `omnivideo.py voice --use <line> <version>`: the reading kept in `history/voice/<line>/<version>/` becomes current again and the current one goes to the history; refuses a version of another text and while the video's voice is running |
573
+ | `POST /api/videos/<slug>/line/text` | `{ line, text }` → the line's `text` in `scenes.json`; a line with voice gets the new reading right away (`voice --regenerate`). A text that removes the word an `enters_with` points at is not saved: it becomes a request to the video's session (`chatWith`), which saves the line, changes the beat with it and generates the voice |
574
+ | `POST /api/videos/<slug>/publish/text` | `{ redo }` → `publish.mjs text`: title, description, post caption and tags from the script (Claude writes them; without it, they come from the script). Without `redo`, only what is empty. The Publicar stage calls it by itself when the video has no text yet |
575
+ | `POST /api/videos/<slug>/publish` | `{ title, description, caption, privacy, tiktok_privacy, youtube, instagram, tiktok, republish }` → text in `publication.json` and `publish.mjs --confirm` (waits for the upload; one per video) |
576
+ | `POST /api/videos/<slug>/archive` | `{ archive }` (default `true`) → writes or deletes `archived.json` (`{ at }`) in the video folder. Archiving takes it off the work list, deleting nothing; refuses a video with a scheduled post or a task running/queued. Only the screen reads the file: no pipeline stage does |
577
+ | `POST /api/videos/<slug>/thumbnail` | `{ mode: "random" }` or `{ mode: "manual", instruction }` → writes the request to `thumbnail/request.json` (`random` or `manual`) and asks the video's Claude for the three random ones (v1–v3) or the manual version (v4) (skill `av-thumbnail`). `{ mode: "change", version, instruction }` changes an already created version (keeps the previous HTML and PNG in `history/` and asks Claude only for the change, preserving the rest). Refuses while the video's session is running. Generates nothing by itself: only with this request |
578
+ | `POST /api/videos/<slug>/thumbnail/choose` | `{ version: "v1"\|"v2"\|"v3"\|"v4"\|"none" }` → `thumbnail.mjs choose`: the version becomes `thumbnail/thumbnail.png`, the cover that `publish.mjs` sends; `none` removes the selection |
579
+ | `GET /api/publish/accounts` | `publish.mjs status --json`, cached for 5 min (`?refresh` asks again) |
580
+ | `GET /api/queue` | `{ running, waiting, recent }` |
581
+ | `GET /api/queue/<id>/log` | SSE: the log so far and what arrives (`log`), and `end` when the task finishes |
582
+ | `GET /api/services` | VoiceStudio, Premiere, memory |
583
+ | `GET /api/videos/<slug>/captions` | captions with id and scene, resolved position, the format's safe areas, anchors and styles |
584
+ | `GET /api/videos/<slug>/captions/page` | the video's `captions.html` (`?preview=1` marks the safe area and accepts `postMessage({ position, t })`) |
585
+ | `GET /api/videos/<slug>/captions/frame?t=` | JPEG of the motion at second `t` of the video, from the scene's MP4 |
586
+ | `POST /api/videos/<slug>/captions/position` | `{ scope: "video" \| "scene" \| "caption", target?, value }` → `captions` in `scenes.json`; `value: null` removes a scene's or caption's adjustment |
587
+ | `GET /api/videos/<slug>/sound` | sound timeline: total, scenes, lines (video time), sound places (each beat with its instant and each scene change, with the sounds), the bed, the catalog (kit by role and the video's `sfx/`, measured) and the export's base MP4 |
588
+ | `POST /api/videos/<slug>/sound/plan` | `{ places, bed }` (draft) → the `sound.mjs` plan with the URL of each finished WAV under `/sound-draft/<slug>/…`; nothing is written to the video |
589
+ | `POST /api/videos/<slug>/sound` | `{ places, bed }` → `sound` on the beats, `transition.sound` and `bed` in `scenes.json` (validated; defaults are dropped) |
590
+ | `POST /api/videos/<slug>/sound/reassemble` | runs `assemble.mjs` (waits for its turn in the queue) |
591
+ | `GET /kit/<name>` | MP3 from the global kit (`kit/sfx/`), for the waveform |
592
+ | `GET /api/usage` | `tools/usage.py summary --json`: Fish balance, Cartesia quota, usage per video and latest generations (only reads the local log) |
593
+ | `POST /api/usage/refresh` | the same with `--live`: asks the platforms now and writes the reading to `.usage/` |
594
+ | `GET /api/analytics` | `tools/analysis.mjs context` (account, medians at the current reading and at the 24 h and 72 h milestones, also per thousand views, the channel's hour-by-hour `progression` and the `publications` in it, the playbook's `before` range, each Reel with metrics, strategy and hook, the agent) plus the text of the latest channel analysis |
595
+ | `GET /api/analytics/reels/<slug>` | one Reel from the context, with the `hourly` series from metrics.json, the medians and its `analysis.md` |
596
+ | `POST /api/analytics/collect` | `metrics.mjs collect --force`: redoes the current reading of all of them (GET-only on the API) |
597
+ | `POST /api/analytics/agent` | `{ enable }` → installs or uninstalls the metrics agent (launchd, every 4 hours) |
598
+ | `POST /api/analytics/analyze` | `{ slug?, focus? }` → the `av-analysis` skill in the analysis session (resumes the stopped one, or opens one) |
599
+ | `POST /api/analytics/claude` | `{ message }` → replies to the analysis session (or opens one with the question) |
600
+ | `GET /api/settings` | what the Settings screen edits; keys only masked, and the accounts of each Claude folder |
601
+ | `POST /api/settings/key` | `{ name, value }` → `FISH_API_KEY`, `CARTESIA_API_KEY` or `CARTESIA_ADMIN_API_KEY` in `.env` (`""` removes) |
602
+ | `POST /api/settings/default-voice` | `{ provider }` → `fish`, `cartesia` or `voicestudio` moves to the front of `voice.order`; the others stay as fallbacks in the order they were |
603
+ | `POST /api/settings/voice-profile` | `{ id?, name, order?, fish?, cartesia?, voicestudio? }` → creates (without `id`) or edits an item of `voice.profiles`; at least one provider (Fish: 32 hex; Cartesia: UUID; VoiceStudio: profile name); `order` is the priority among them; the first voice created becomes the active one |
604
+ | `POST /api/settings/voice-profile/default` | `{ id }` → `voice.default_profile` (the active voice) |
605
+ | `POST /api/settings/voice-profile/remove` | `{ id }` → removes the profile from `voice.profiles` (if it was the default, the first remaining one takes over) |
606
+ | `POST /api/settings/voice-id` | `{ provider, voice_id }` → `voice.<provider>.voice_id` (Fish: 32 hex; Cartesia: UUID) |
607
+ | `POST /api/settings/fish-model` | `{ model, fallback }` → `voice.fish.model` and `voice.fish.fallback_model` (`s2.1-pro-free` or `s2.1-pro`; `fallback: null` turns it off) |
608
+ | `POST /api/settings/fish-price` | `{ value }` → `usage.fish.usd_per_million_bytes` |
609
+ | `POST /api/settings/models` | `{ orchestrator?, reference?, stages?: { <skill>: { model, effort? } } }` → `models` in the config and `tools/models.mjs apply` on the skills |
610
+ | `POST /api/settings/claude` | `{ config_dir }` → `studio.claude.config_dir` (`null` = `~/.claude`) |
611
+ | `GET /api/events` | SSE: `queue`, `services` and `sessions` with the data, `video` with the slug that changed, `reference` with the name |
612
+ | `GET /media/<slug>/<path>` | a video file, with Range (206) for the players; only media and text, never outside the video folder |
613
+
614
+ ## Files
615
+
616
+ ```
617
+ studio/studio.ts entry: args, HTML routes, Bun.serve; the rest goes to the API
618
+ studio/api/app.ts the Hono app: local-only guard, every router, errors (AppType)
619
+ studio/api/routes/ one thin router per domain: validate (zod) → service → respond
620
+ studio/api/services/ videos (status, times, media, history), queue (reading
621
+ and log), services,
622
+ approval (motion script scenes), events,
623
+ sessions (claude -p, state, transcript), create (video,
624
+ reference, chat, verdict), references, usage,
625
+ analytics (metrics, collect, agent, Claude analysis),
626
+ sound (sound timeline: data, draft, save), carousel…
627
+ studio/api/test/ bun test: guard, validation, routes on a fixture root
628
+ (OMNIVIDEO_ROOT points the services at another root)
629
+ studio/shared/ zod schemas (shared/schemas) and response types
630
+ (shared/types), used by the API and the web
631
+ studio/web/ React (TanStack Router + Query, typed hono/client in lib/api.ts):
632
+ pages/VideosPage (overview),
633
+ NewVideoPage, VideoPage (stepper, process, result,
634
+ StageDrawer, SoundTimeline, one component per stage),
635
+ components/SessionPanel (session and reply), ReferencesPage,
636
+ components/StageStepper, lib/stages.ts (what each stage is),
637
+ QueuePage, SettingsPage (UsageCategory), AnalyticsPage,
638
+ components/ui/chart.tsx (shadcn charts on Recharts, no Tailwind),
639
+ components/ApprovalNotice (sound and title)
640
+ .sound-draft/ WAVs of the sound timeline draft (generated)
641
+ tools/approval.mjs scene approval: open, wait, decide, deadline
642
+ .sessions/ one folder per Claude session (outside git)
643
+ .usage/events.jsonl usage log of the paid voices (outside git)
644
+ tools/usage.py usage logging and reading (Fish, Cartesia)
645
+ tools/metrics.mjs Reel metrics; tools/analysis.mjs gathers what the Analytics screen shows
646
+ tools/job_queue.py the queue protocol (and the guardian of the Node tools)
647
+ tools/job-queue.mjs the turn for the Node tools
648
+ tools/history.mjs, .py previous versions of the results
649
+ .queue/ tickets, logs and the lock (outside git)
650
+ videos/<slug>/history/ previous versions of each video (outside git)
651
+ ```
652
+
653
+ ## For later (the architecture already allows it)
654
+
655
+ - **Studio MCP for the chat**, with a tool that waits for the approval made
656
+ on the screen: it means watching `script-approval.json` and
657
+ `motion/approval.json`, which are already where the decisions land.
658
+ - **Actions from the screen** (regenerate a line, fix pronunciation,
659
+ re-render a scene, assemble, export): the studio calls the tool with
660
+ `OMNIVIDEO_ORIGIN=studio`, and it joins the queue by itself, like any other.
661
+ - **Timeline** with waveform and beats: `/api/videos/<slug>` already returns
662
+ the resolved times and the WAVs.
663
+ - **Attaching a file to the request** (PDF, style video) straight into
664
+ `source/`: today it comes in by path, and the POST only accepts JSON.
665
+ - **Granting permission from the screen**: the denied notice would get a
666
+ button that writes the rule to `studio.claude.allow`.