@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,244 @@
1
+ /* ============================================================================
2
+ Publishing decisions shared by the CLI and a future cloud runner: the
3
+ publish config with its defaults, the post texts (footer, hashtags, the
4
+ platforms' limits), the per-platform gate before uploading and the record
5
+ written back to publication.json. Pure.
6
+ ============================================================================ */
7
+
8
+ export const PLATFORMS = ["youtube", "instagram", "tiktok"];
9
+ export const PRIVACIES = ["private", "unlisted", "public"];
10
+ export const TIKTOK_PRIVACIES = ["SELF_ONLY", "MUTUAL_FOLLOW_FRIENDS", "FOLLOWER_OF_CREATOR", "PUBLIC_TO_EVERYONE"];
11
+ export const TEXT_FIELDS = ["title", "description", "caption", "tags"];
12
+
13
+ /**
14
+ * publish.* in the config with the defaults filled in.
15
+ * @param {Record<string, any>} [cfg] the config's `publish`
16
+ */
17
+ export function publishConfig(cfg = {}) {
18
+ const P = {
19
+ auto: { approved: false, platforms: [], requires_approved_preview: true, ...(cfg.auto ?? {}) },
20
+ text: { footer_youtube: "", footer_instagram: "", footer_tiktok: "", hashtags: [], ...(cfg.text ?? {}) },
21
+ youtube: {
22
+ privacy: "public",
23
+ audited: false,
24
+ category: "22",
25
+ language: "pt-BR",
26
+ made_for_kids: false,
27
+ synthetic_content: false,
28
+ ...(cfg.youtube ?? {}),
29
+ },
30
+ instagram: { api_version: "v23.0", share_to_feed: true, cover_at_s: null, ...(cfg.instagram ?? {}) },
31
+ tiktok: {
32
+ mode: "direct",
33
+ privacy: "PUBLIC_TO_EVERYONE",
34
+ audited: false,
35
+ comments: true,
36
+ duet: false,
37
+ stitch: false,
38
+ ai_content: true,
39
+ cover_at_s: null,
40
+ ...(cfg.tiktok ?? {}),
41
+ },
42
+ schedule: { max_delay_h: 12, attempts: 3, interval_min: 10, ...(cfg.schedule ?? {}) },
43
+ };
44
+ /* a config not yet migrated may still say "rascunho"/"direto": never publish directly by mistake */
45
+ P.tiktok.mode = { rascunho: "draft", direto: "direct" }[/** @type {string} */ (P.tiktok.mode)] ?? P.tiktok.mode;
46
+ return P;
47
+ }
48
+
49
+ /** @typedef {ReturnType<typeof publishConfig>} PublishConfig */
50
+
51
+ const HASHTAG = /#[\p{L}\p{N}_]+/gu;
52
+
53
+ /**
54
+ * The footer and the fixed hashtags go at the end, without repeating what the text has.
55
+ * @param {string} text @param {string | null | undefined} footer @param {string[]} hashtags
56
+ */
57
+ export function withFixed(text, footer, hashtags) {
58
+ const present = new Set((text.match(HASHTAG) ?? []).map(h => h.toLowerCase()));
59
+ const tags = hashtags
60
+ .map(h => `#${String(h).trim().replace(/^#/, "")}`)
61
+ .filter(h => h.length > 1 && !present.has(h.toLowerCase()));
62
+ const f = String(footer ?? "").trim();
63
+ return [text, f && !text.includes(f) ? f : "", tags.join(" ")].filter(Boolean).join("\n\n");
64
+ }
65
+
66
+ /**
67
+ * The Instagram caption's own limits (2200 characters, 30 hashtags).
68
+ * @param {string} caption
69
+ * @returns {string | null} the problem, or null
70
+ */
71
+ export function instagramCaptionProblem(caption) {
72
+ if (caption.length > 2200) return `legenda do Instagram com ${caption.length} caracteres (até 2200)`;
73
+ if ((caption.match(HASHTAG) ?? []).length > 30) return "legenda do Instagram com mais de 30 hashtags";
74
+ return null;
75
+ }
76
+
77
+ /**
78
+ * Each platform's texts and their limits, checked before uploading anything.
79
+ * @param {{ pub: Record<string, any>, defaultTitle: () => string, text: PublishConfig["text"] }} p
80
+ */
81
+ export function metadata({ pub, defaultTitle, text }) {
82
+ const title = String(pub.title ?? defaultTitle()).trim();
83
+ const base = String(pub.description ?? "").trim();
84
+ const tags = (pub.tags ?? []).map((/** @type {unknown} */ t) => String(t).trim()).filter(Boolean);
85
+ const description = withFixed(base, text.footer_youtube, text.hashtags);
86
+ const social =
87
+ String(pub.caption ?? pub.caption_instagram ?? "").trim() || [title, base].filter(Boolean).join("\n\n");
88
+ const caption = withFixed(social, text.footer_instagram, text.hashtags);
89
+ const tiktokCaption = withFixed(social, text.footer_tiktok, text.hashtags);
90
+ const problems = [];
91
+ if (!title) problems.push("título vazio");
92
+ if (title.length > 100) problems.push(`título com ${title.length} caracteres (YouTube: até 100)`);
93
+ if (/[<>]/.test(title + description)) problems.push("o YouTube não aceita < nem > no título ou na descrição");
94
+ if (new TextEncoder().encode(description).byteLength > 5000) problems.push("descrição passa de 5000 bytes (YouTube)");
95
+ if (tags.join(",").length > 500) problems.push("tags passam de 500 caracteres (YouTube)");
96
+ if (caption.length > 2200) problems.push(`legenda do Instagram com ${caption.length} caracteres (até 2200)`);
97
+ if ((caption.match(HASHTAG) ?? []).length > 30) problems.push("legenda do Instagram com mais de 30 hashtags");
98
+ /* TikTok counts UTF-16 units, which is JS .length */
99
+ if (tiktokCaption.length > 2200) problems.push(`legenda do TikTok com ${tiktokCaption.length} caracteres (até 2200)`);
100
+ return { title, description, tags, caption, tiktokCaption, problems };
101
+ }
102
+
103
+ /**
104
+ * The title when publication.json has none: scenes.json's, the script's heading, or the folder name.
105
+ * @param {{ scenesTitle?: string | null, script: string, dirName: string }} p
106
+ */
107
+ export function defaultTitle({ scenesTitle, script, dirName }) {
108
+ if (scenesTitle) return scenesTitle;
109
+ /* the `# Roteiro · ` heading is PT content parsed as is */
110
+ return script.match(/^#\s+(?:Roteiro\s*·\s*)?(.+)$/m)?.[1]?.trim() ?? dirName.replace(/^\d{4}-\d{2}-\d{2}-/, "");
111
+ }
112
+
113
+ /**
114
+ * Without a model: the script's title, the thesis as description and the hook (first quote) as caption.
115
+ * @param {string} script @param {string} title
116
+ */
117
+ export function textFromScript(script, title) {
118
+ /** @param {string} s */
119
+ const clean = s => s.replace(/\*\*|`/g, "").trim();
120
+ const thesis = script.match(/^\*\*Tese:\*\*\s*(.+)$/m)?.[1];
121
+ const hook = script.match(/^>\s*(.+)$/m)?.[1];
122
+ return { title, description: thesis ? clean(thesis) : "", caption: hook ? clean(hook) : "", tags: [] };
123
+ }
124
+
125
+ /** The prompt that turns the script (on stdin) into the post texts. */
126
+ export const TEXT_PROMPT = `Você escreve o texto de publicação de um vídeo curto a partir do roteiro dele, que vem na entrada.
127
+ Responda só com um objeto JSON, sem markdown nem comentário:
128
+ {"title": "...", "description": "...", "caption": "...", "tags": ["..."]}
129
+
130
+ - title (YouTube): até 90 caracteres, a promessa do vídeo, em português do Brasil. Sem < nem >, sem hashtag, sem aspas em volta. Não é o nome do arquivo nem começa com "Roteiro".
131
+ - description (YouTube): 2 a 4 linhas curtas com o que a pessoa leva do vídeo. Não repete o título palavra por palavra. Sem < nem >.
132
+ - caption (Instagram e TikTok): 1 a 3 linhas no tom de quem fala, abrindo pelo gancho do vídeo, e até 3 hashtags do assunto no fim.
133
+ - tags: até 8 palavras-chave do assunto, sem #.
134
+
135
+ Use só o que o roteiro diz: não invente número, nome, dado nem promessa. Ignore as notas de produção (ajustes para a voz, fontes, tabela de duração). Não acrescente rodapé, link nem chamada para seguir: isso entra depois, sozinho.`;
136
+
137
+ /**
138
+ * The model's answer to TEXT_PROMPT, cleaned. Throws when it is not usable.
139
+ * @param {string} out
140
+ */
141
+ export function parseModelText(out) {
142
+ const raw = /\{[\s\S]*\}/.exec(out)?.[0];
143
+ let j;
144
+ try {
145
+ j = JSON.parse(/** @type {string} */ (raw));
146
+ } catch {
147
+ throw new Error(`claude não devolveu JSON: ${out.trim().slice(0, 200)}`);
148
+ }
149
+ const title = String(j.title ?? "")
150
+ .replace(/[<>]/g, "")
151
+ .trim()
152
+ .slice(0, 100);
153
+ if (!title) throw new Error("claude devolveu título vazio");
154
+ return {
155
+ title,
156
+ description: String(j.description ?? "")
157
+ .replace(/[<>]/g, "")
158
+ .trim(),
159
+ caption: String(j.caption ?? "").trim(),
160
+ tags: (Array.isArray(j.tags) ? j.tags : [])
161
+ .map((/** @type {unknown} */ t) => String(t).replace(/^#/, "").trim())
162
+ .filter(Boolean)
163
+ .slice(0, 8),
164
+ };
165
+ }
166
+
167
+ /**
168
+ * Which text fields are to be (re)written. The old Instagram caption is moved to `caption` (mutates `pub`).
169
+ * @param {Record<string, any>} pub @param {boolean} redo
170
+ */
171
+ export function missingText(pub, redo) {
172
+ if (pub.caption == null && pub.caption_instagram) {
173
+ pub.caption = pub.caption_instagram;
174
+ delete pub.caption_instagram;
175
+ }
176
+ /** @param {string} k */
177
+ const empty = k => (k === "tags" ? !pub.tags?.length : !String(pub[k] ?? "").trim());
178
+ return TEXT_FIELDS.filter(k => redo || empty(k));
179
+ }
180
+
181
+ /**
182
+ * Before uploading to one platform: skip (already up), refuse (limits) or go, with warnings.
183
+ * @param {{ platform: string, before: Record<string, any> | undefined, video: { w: number, h: number, duration: number }, again: boolean,
184
+ * format: (iso: string) => string }} p
185
+ * @returns {{ action: "skip", message: string } | { action: "refuse", message: string, warnings: string[] } | { action: "go", warnings: string[] }}
186
+ */
187
+ export function platformGate({ platform, before, video, again, format }) {
188
+ if (before?.id && !again)
189
+ return {
190
+ action: "skip",
191
+ message: `${platform}: já publicado em ${format(before.published_at)}: ${before.url ?? before.id} (--again sobe outra vez)`,
192
+ };
193
+ const warnings = [];
194
+ if (platform !== "youtube" && video.h <= video.w)
195
+ warnings.push(`aviso: o vídeo não é vertical; o ${platform === "tiktok" ? "TikTok" : "Reels"} mostra com faixas`);
196
+ if (platform === "instagram" && (video.duration < 3 || video.duration > 900))
197
+ return {
198
+ action: "refuse",
199
+ message: `instagram: Reels pela API vai de 3 s a 15 min; este tem ${video.duration.toFixed(1)} s`,
200
+ warnings,
201
+ };
202
+ if (platform === "tiktok" && video.duration < 3)
203
+ return {
204
+ action: "refuse",
205
+ message: `tiktok: o vídeo precisa de pelo menos 3 s; este tem ${video.duration.toFixed(1)} s`,
206
+ warnings,
207
+ };
208
+ return { action: "go", warnings };
209
+ }
210
+
211
+ /**
212
+ * The previous ids when a platform goes up again.
213
+ * @param {Record<string, any> | undefined} before
214
+ */
215
+ export const previousOf = before =>
216
+ before?.id
217
+ ? [...(before.previous ?? []), { id: before.id, url: before.url, published_at: before.published_at }]
218
+ : before?.previous;
219
+
220
+ /**
221
+ * The platform's entry in publication.json after an upload.
222
+ * @param {{ result: Record<string, any>, before: Record<string, any> | undefined, mp4: string, mp4Mtime: number,
223
+ * scheduledFor?: string | null, now: number }} p
224
+ */
225
+ export function publishedEntry({ result, before, mp4, mp4Mtime, scheduledFor, now }) {
226
+ const previous = previousOf(before);
227
+ return {
228
+ ...result,
229
+ published_at: new Date(now).toISOString(),
230
+ mp4,
231
+ mp4_mtime: mp4Mtime,
232
+ ...(scheduledFor ? { scheduled_for: scheduledFor } : {}),
233
+ ...(previous?.length ? { previous } : {}),
234
+ };
235
+ }
236
+
237
+ /**
238
+ * The platform's entry after a failed attempt: what was there, plus the error.
239
+ * @param {Record<string, any> | undefined} before @param {string} message @param {number} now
240
+ */
241
+ export const failedEntry = (before, message, now) => ({
242
+ ...(before ?? {}),
243
+ last_error: { at: new Date(now).toISOString(), message },
244
+ });
@@ -0,0 +1,191 @@
1
+ /* ============================================================================
2
+ Scheduled publishing: the decisions, without the runner. A schedule lives in
3
+ publication.json (`schedule`); these functions say when it is due, when it
4
+ is missed, how an attempt starts and what its outcome becomes (done, retry
5
+ later, or failed). The caller runs the upload (a child publish.mjs on the
6
+ CLI, the core upload in a cloud function) and persists the result.
7
+ ============================================================================ */
8
+
9
+ import { HOUR } from "../http.mjs";
10
+
11
+ /**
12
+ * @typedef {object} Schedule
13
+ * @property {string} when ISO
14
+ * @property {string[]} platforms
15
+ * @property {string} privacy
16
+ * @property {string} tiktok_privacy
17
+ * @property {boolean} republish
18
+ * @property {"scheduled" | "publishing" | "failed" | "missed"} state
19
+ * @property {number} attempts
20
+ * @property {string} [created_at]
21
+ * @property {string} [origin]
22
+ * @property {number} [pid]
23
+ * @property {string} [started_at]
24
+ * @property {string} [next_attempt]
25
+ * @property {string} [rescheduled_at]
26
+ * @property {{ at: string, message: string }} [last_error]
27
+ */
28
+
29
+ /**
30
+ * @typedef {object} ScheduleSettings publish.schedule in the config
31
+ * @property {number} max_delay_h past this delay a never-tried schedule is missed
32
+ * @property {number} attempts
33
+ * @property {number} interval_min between attempts
34
+ */
35
+
36
+ /** The defaults of publish.schedule. */
37
+ export const SCHEDULE_DEFAULTS = { max_delay_h: 12, attempts: 3, interval_min: 10 };
38
+
39
+ /**
40
+ * "2026-10-14 18:00" or "2026-10-14T18:00" is the local time of the runtime;
41
+ * with Z or an offset, the text's. Throws with the CLI's message when invalid
42
+ * or less than 1 min ahead.
43
+ * @param {string | undefined} s
44
+ * @param {{ now: number, format: (d: Date) => string }} p
45
+ * @returns {Date}
46
+ */
47
+ export function parseWhen(s, { now, format }) {
48
+ if (!s) throw new Error('diga quando: --when "2026-10-14 18:00" (hora desta máquina)');
49
+ const t = String(s)
50
+ .trim()
51
+ .replace(/^(\d{4}-\d{2}-\d{2})\s+/, "$1T");
52
+ if (!/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}/.test(t)) throw new Error(`quando: use AAAA-MM-DD HH:MM (veio "${s}")`);
53
+ const d = new Date(t);
54
+ if (Number.isNaN(d.getTime())) throw new Error(`quando: data inválida "${s}"`);
55
+ if (d.getTime() < now + 60_000)
56
+ throw new Error(`${format(d)} já passou (ou é daqui a menos de 1 min): para agora, publique sem agendar`);
57
+ return d;
58
+ }
59
+
60
+ /**
61
+ * A new schedule for a confirmed request.
62
+ * @param {{ when: Date, platforms: string[], privacy: string, tiktokPrivacy: string, republish: boolean, now: number, origin: string }} p
63
+ * @returns {Schedule}
64
+ */
65
+ export const newSchedule = ({ when, platforms, privacy, tiktokPrivacy, republish, now, origin }) => ({
66
+ when: when.toISOString(),
67
+ platforms,
68
+ privacy,
69
+ tiktok_privacy: tiktokPrivacy,
70
+ republish,
71
+ state: "scheduled",
72
+ attempts: 0,
73
+ created_at: new Date(now).toISOString(),
74
+ origin,
75
+ });
76
+
77
+ /**
78
+ * Another time for the same request: platforms, privacy and text stay; the attempt count restarts.
79
+ * @param {Schedule} sch @param {Date} when @param {number} now
80
+ * @returns {Schedule}
81
+ */
82
+ export function reschedule(sch, when, now) {
83
+ const { pid, last_error, next_attempt, started_at, ...rest } = sch;
84
+ return {
85
+ ...rest,
86
+ when: when.toISOString(),
87
+ state: "scheduled",
88
+ attempts: 0,
89
+ rescheduled_at: new Date(now).toISOString(),
90
+ };
91
+ }
92
+
93
+ /**
94
+ * Published by hand where it was scheduled: the platform leaves the schedule
95
+ * (the whole schedule goes when none is left). Mutates `pub`.
96
+ * @param {{ schedule?: Schedule }} pub @param {string} platform
97
+ * @returns {boolean} whether the platform was in the schedule
98
+ */
99
+ export function dropPlatform(pub, platform) {
100
+ if (!pub.schedule?.platforms?.includes(platform)) return false;
101
+ pub.schedule.platforms = pub.schedule.platforms.filter(p => p !== platform);
102
+ if (!pub.schedule.platforms.length) delete pub.schedule;
103
+ return true;
104
+ }
105
+
106
+ /**
107
+ * Is it time? "publishing" without a live process is a run that died midway:
108
+ * it counts as a new attempt.
109
+ * @param {Schedule | undefined} sch
110
+ * @param {{ now: number, alive: (pid: number | undefined) => boolean, settings: ScheduleSettings }} p
111
+ * @returns {{ action: "skip" } | { action: "run" } | { action: "missed", message: string }}
112
+ */
113
+ export function dueAction(sch, { now, alive, settings }) {
114
+ if (!sch) return { action: "skip" };
115
+ if (sch.state === "publishing" && alive(sch.pid)) return { action: "skip" };
116
+ if (!["scheduled", "publishing"].includes(sch.state)) return { action: "skip" };
117
+ const at = Date.parse(sch.when);
118
+ if (!(at <= now)) return { action: "skip" };
119
+ if (sch.next_attempt && Date.parse(sch.next_attempt) > now) return { action: "skip" };
120
+ const delay = (now - at) / HOUR;
121
+ if (!sch.attempts && delay > settings.max_delay_h)
122
+ return {
123
+ action: "missed",
124
+ message: `o Mac estava dormindo ou desligado na hora: ${delay.toFixed(1)} h de atraso passa do limite de ${settings.max_delay_h} h (publish.schedule.max_delay_h)`,
125
+ };
126
+ return { action: "run" };
127
+ }
128
+
129
+ /**
130
+ * Marks a schedule missed. Mutates and returns it.
131
+ * @param {Schedule} sch @param {string} message @param {number} now
132
+ */
133
+ export function markMissed(sch, message, now) {
134
+ sch.state = "missed";
135
+ sch.last_error = { at: new Date(now).toISOString(), message };
136
+ return sch;
137
+ }
138
+
139
+ /**
140
+ * Marks the start of an attempt. Mutates and returns it.
141
+ * @param {Schedule} sch @param {{ pid: number, startedAt: string }} p
142
+ */
143
+ export function startAttempt(sch, { pid, startedAt }) {
144
+ Object.assign(sch, { state: "publishing", pid, started_at: startedAt, attempts: (sch.attempts ?? 0) + 1 });
145
+ delete sch.next_attempt;
146
+ return sch;
147
+ }
148
+
149
+ /**
150
+ * The reason of a failed attempt, from the child's stderr (warnings and the
151
+ * "erro: " prefix dropped; the last three lines).
152
+ * @param {{ stderr?: string | null, error?: Error | null, status?: number | null }} p
153
+ */
154
+ export function failureReason({ stderr, error, status }) {
155
+ const lines = (stderr ?? "")
156
+ .split("\n")
157
+ .map(l => l.trim().replace(/^erro: /, ""))
158
+ .filter(l => l && !l.startsWith("aviso:"));
159
+ return error?.message ?? (lines.slice(-3).join(" · ") || `publish.mjs saiu com ${status}`);
160
+ }
161
+
162
+ /**
163
+ * What an attempt became. `after` is publication.json read after the upload.
164
+ * A platform is done if it went up in this run, or was already up and was not
165
+ * meant to go up again; the rest retries every interval_min until attempts runs out.
166
+ * @param {{ sch: Schedule, after: Record<string, any>, startedAt: string, reason: () => string, now: number, settings: ScheduleSettings }} p
167
+ * @returns {{ done: true } | { done: false, schedule: Schedule, missing: string[], giveUp: boolean, reason: string }}
168
+ */
169
+ export function attemptOutcome({ sch, after, startedAt, reason, now, settings }) {
170
+ const base = { ...sch, ...(after.schedule ?? {}) };
171
+ /** @param {string} p */
172
+ const done = p => (after[p]?.published_at ?? "") >= startedAt || (!sch.republish && after[p]?.id);
173
+ const missing = sch.platforms.filter(p => !done(p));
174
+ if (!missing.length) return { done: true };
175
+ const why = reason();
176
+ const giveUp = base.attempts >= settings.attempts;
177
+ delete base.pid;
178
+ return {
179
+ done: false,
180
+ missing,
181
+ giveUp,
182
+ reason: why,
183
+ schedule: {
184
+ ...base,
185
+ platforms: missing,
186
+ state: giveUp ? "failed" : "scheduled",
187
+ last_error: { at: new Date(now).toISOString(), message: why },
188
+ ...(giveUp ? {} : { next_attempt: new Date(now + settings.interval_min * 60_000).toISOString() }),
189
+ },
190
+ };
191
+ }
@@ -0,0 +1,134 @@
1
+ /* The studio kept on by launchd (macOS): `bun studio.ts` in <code root>/studio, started at login and again
2
+ whenever it stops (30 s apart, so a studio already open in a terminal holding the port just waits its
3
+ turn). PATH goes along because launchd starts with a minimal one, without claude, ffmpeg or the venv's
4
+ python; OMNI_HOME goes along when the workspace is not the code root (tools/lib/paths.mjs).
5
+
6
+ Used by `tools/runner.mjs service` (this repo) and `omni up` (the installed CLI). Throws Errors in
7
+ Portuguese; the callers print them. */
8
+
9
+ import { spawnSync } from "node:child_process";
10
+ import { existsSync, mkdirSync, unlinkSync, writeFileSync } from "node:fs";
11
+ import { homedir } from "node:os";
12
+ import { dirname, join } from "node:path";
13
+ import { CODE_ROOT, DATA_ROOT, homeEnvPlist } from "./paths.mjs";
14
+
15
+ export const AGENT = "com.omnivideo.studio";
16
+ export const PLIST = join(homedir(), "Library", "LaunchAgents", `${AGENT}.plist`);
17
+ export const LOG_FILE = join(DATA_ROOT, ".runner", "studio.log");
18
+ const domain = () => `gui/${process.getuid?.()}`;
19
+ const launchctl = (/** @type {string[]} */ ...a) => spawnSync("launchctl", a, { encoding: "utf8" });
20
+
21
+ /** the bun to run the studio with: the CLI package's own (the `bun` dependency), else the one installed */
22
+ export function bunPath(codeRoot = CODE_ROOT) {
23
+ const found = spawnSync("/bin/sh", ["-lc", "command -v bun"], { encoding: "utf8" }).stdout.trim();
24
+ return [
25
+ join(codeRoot, "node_modules", ".bin", "bun"),
26
+ join(codeRoot, "..", "..", ".bin", "bun"),
27
+ found,
28
+ join(homedir(), ".bun", "bin", "bun"),
29
+ "/opt/homebrew/bin/bun",
30
+ "/usr/local/bin/bun",
31
+ ].find(p => p && existsSync(p));
32
+ }
33
+
34
+ /** what launchd says about the studio's agent */
35
+ export function service() {
36
+ const installed = existsSync(PLIST);
37
+ const printed = installed ? launchctl("print", `${domain()}/${AGENT}`) : null;
38
+ const pid = printed?.status === 0 ? Number(/\bpid = (\d+)/.exec(printed.stdout)?.[1] ?? 0) : 0;
39
+ return { installed, loaded: printed?.status === 0, pid: pid || null, log: LOG_FILE };
40
+ }
41
+
42
+ /** the plist the agent would have now: reinstalling only when it changed keeps a running studio running */
43
+ function plist(/** @type {string} */ bun, /** @type {string} */ codeRoot) {
44
+ const x = (/** @type {string} */ v) => v.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
45
+ const path = [
46
+ ...new Set([
47
+ dirname(bun),
48
+ dirname(process.execPath),
49
+ "/opt/homebrew/bin",
50
+ "/usr/local/bin",
51
+ ...(process.env.PATH ?? "").split(":"),
52
+ "/usr/bin",
53
+ "/bin",
54
+ "/usr/sbin",
55
+ "/sbin",
56
+ ]),
57
+ ]
58
+ .filter(Boolean)
59
+ .join(":");
60
+ return `<?xml version="1.0" encoding="UTF-8"?>
61
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
62
+ <plist version="1.0">
63
+ <dict>
64
+ <key>Label</key><string>${AGENT}</string>
65
+ <key>ProgramArguments</key>
66
+ <array>
67
+ <string>${x(bun)}</string>
68
+ <string>studio.ts</string>
69
+ </array>
70
+ <key>WorkingDirectory</key><string>${x(join(codeRoot, "studio"))}</string>
71
+ <key>EnvironmentVariables</key>
72
+ <dict><key>PATH</key><string>${x(path)}</string>${homeEnvPlist(x)}</dict>
73
+ <key>RunAtLoad</key><true/>
74
+ <key>KeepAlive</key><true/>
75
+ <key>ThrottleInterval</key><integer>30</integer>
76
+ <key>StandardOutPath</key><string>${x(LOG_FILE)}</string>
77
+ <key>StandardErrorPath</key><string>${x(LOG_FILE)}</string>
78
+ </dict>
79
+ </plist>
80
+ `;
81
+ }
82
+
83
+ /**
84
+ * Installs (or replaces) the agent. With `onlyIfChanged`, an agent already loaded with the same plist is
85
+ * left alone, so `omni up` on every session start never restarts a studio that is working.
86
+ * @param {{ codeRoot?: string, onlyIfChanged?: boolean, log?: (line: string) => void }} [o]
87
+ * @returns {{ changed: boolean }}
88
+ */
89
+ export function serviceInstall({ codeRoot = CODE_ROOT, onlyIfChanged = false, log = () => {} } = {}) {
90
+ if (process.platform !== "darwin") throw new Error("o serviço é do macOS (launchd)");
91
+ const bun = bunPath(codeRoot);
92
+ if (!bun) throw new Error("não achei o bun: instale com curl -fsSL https://bun.sh/install | bash");
93
+ const text = plist(bun, codeRoot);
94
+ let current = "";
95
+ try {
96
+ current = existsSync(PLIST) ? String(spawnSync("cat", [PLIST], { encoding: "utf8" }).stdout) : "";
97
+ } catch {
98
+ current = "";
99
+ }
100
+ if (onlyIfChanged && current === text && service().loaded) return { changed: false };
101
+ /* this repo pins the studio's dependencies with its lockfile; the CLI package has them installed by npm */
102
+ if (existsSync(join(codeRoot, "studio", "bun.lock"))) {
103
+ const deps = spawnSync(bun, ["install", "--frozen-lockfile", "--silent"], {
104
+ cwd: join(codeRoot, "studio"),
105
+ stdio: "inherit",
106
+ });
107
+ if (deps.status !== 0) throw new Error("bun install falhou em studio/");
108
+ }
109
+ mkdirSync(dirname(LOG_FILE), { recursive: true });
110
+ mkdirSync(dirname(PLIST), { recursive: true });
111
+ writeFileSync(PLIST, text);
112
+ /* reinstalling replaces what was loaded; launchd sometimes takes a moment to release the old one */
113
+ launchctl("bootout", `${domain()}/${AGENT}`);
114
+ let r = launchctl("bootstrap", domain(), PLIST);
115
+ for (let i = 0; i < 6 && r.status !== 0; i++) {
116
+ spawnSync("sleep", ["0.5"]);
117
+ r = launchctl("bootstrap", domain(), PLIST);
118
+ }
119
+ if (r.status !== 0) throw new Error(`launchctl bootstrap: ${(r.stderr || r.stdout).trim()}`);
120
+ if (spawnSync("lsof", ["-nP", "-iTCP:5432", "-sTCP:LISTEN"], { encoding: "utf8" }).stdout.includes("LISTEN"))
121
+ log("aviso: já há um studio aberto na porta 5432 (num terminal?): feche-o e o serviço assume em até 30 s");
122
+ return { changed: true };
123
+ }
124
+
125
+ export function serviceUninstall() {
126
+ launchctl("bootout", `${domain()}/${AGENT}`);
127
+ if (existsSync(PLIST)) unlinkSync(PLIST);
128
+ }
129
+
130
+ export function serviceRestart() {
131
+ if (!service().loaded) throw new Error("o serviço não está ligado");
132
+ const r = launchctl("kickstart", "-k", `${domain()}/${AGENT}`);
133
+ if (r.status !== 0) throw new Error(`launchctl kickstart: ${(r.stderr || r.stdout).trim()}`);
134
+ }