@crievo/omni 0.0.0-stage → 0.1.0

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 (709) hide show
  1. package/.env.example +41 -0
  2. package/README.md +2 -2
  3. package/bin/omni.mjs +14 -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.test.tsx +30 -0
  247. package/studio/web/components/ui/tooltip.tsx +59 -0
  248. package/studio/web/hooks/useAccounts.ts +27 -0
  249. package/studio/web/hooks/useAdmin.ts +12 -0
  250. package/studio/web/hooks/useAnalytics.ts +21 -0
  251. package/studio/web/hooks/useAvatarAudioReactive.ts +87 -0
  252. package/studio/web/hooks/useBrowserConnect.ts +48 -0
  253. package/studio/web/hooks/useClonePlan.ts +14 -0
  254. package/studio/web/hooks/useConnected.ts +7 -0
  255. package/studio/web/hooks/useLiveQuery.ts +29 -0
  256. package/studio/web/hooks/useMenuCollapsed.ts +32 -0
  257. package/studio/web/hooks/useMicDevices.ts +53 -0
  258. package/studio/web/hooks/useMissingDms.ts +211 -0
  259. package/studio/web/hooks/useMissingSetupSteps.ts +26 -0
  260. package/studio/web/hooks/useNow.ts +11 -0
  261. package/studio/web/hooks/useOmniAgent.ts +278 -0
  262. package/studio/web/hooks/useOmniClaude.ts +124 -0
  263. package/studio/web/hooks/useOmniDock.ts +142 -0
  264. package/studio/web/hooks/useOmniGreeting.ts +60 -0
  265. package/studio/web/hooks/useOmniHotword.ts +128 -0
  266. package/studio/web/hooks/useOmnichat.ts +26 -0
  267. package/studio/web/hooks/useOnboarding.ts +27 -0
  268. package/studio/web/hooks/usePost.ts +23 -0
  269. package/studio/web/hooks/useQueue.ts +10 -0
  270. package/studio/web/hooks/useReferences.ts +20 -0
  271. package/studio/web/hooks/useRuleEditor.ts +101 -0
  272. package/studio/web/hooks/useRunner.ts +24 -0
  273. package/studio/web/hooks/useSaveAction.ts +23 -0
  274. package/studio/web/hooks/useSchedule.ts +11 -0
  275. package/studio/web/hooks/useServices.ts +10 -0
  276. package/studio/web/hooks/useSessions.ts +15 -0
  277. package/studio/web/hooks/useSettings.ts +11 -0
  278. package/studio/web/hooks/useSettingsSection.ts +30 -0
  279. package/studio/web/hooks/useSidePanel.ts +23 -0
  280. package/studio/web/hooks/useSound.ts +30 -0
  281. package/studio/web/hooks/useUsage.ts +11 -0
  282. package/studio/web/hooks/useVideoVoice.ts +36 -0
  283. package/studio/web/hooks/useVideos.ts +21 -0
  284. package/studio/web/hooks/useVoiceProfilePreview.ts +52 -0
  285. package/studio/web/hooks/useVoiceProfiles.ts +7 -0
  286. package/studio/web/hooks/useVoiceRunning.ts +10 -0
  287. package/studio/web/hooks/useVoiceStudioClosed.ts +8 -0
  288. package/studio/web/index.html +19 -0
  289. package/studio/web/lib/agent.ts +7 -0
  290. package/studio/web/lib/alerts.ts +210 -0
  291. package/studio/web/lib/analytics-metrics.ts +351 -0
  292. package/studio/web/lib/api.ts +70 -0
  293. package/studio/web/lib/audio.ts +39 -0
  294. package/studio/web/lib/avatars.ts +41 -0
  295. package/studio/web/lib/calendar.ts +218 -0
  296. package/studio/web/lib/caption-position.ts +249 -0
  297. package/studio/web/lib/carousel.ts +24 -0
  298. package/studio/web/lib/chime.ts +42 -0
  299. package/studio/web/lib/clone-voice.ts +55 -0
  300. package/studio/web/lib/export.ts +11 -0
  301. package/studio/web/lib/files.ts +8 -0
  302. package/studio/web/lib/format.ts +93 -0
  303. package/studio/web/lib/media.ts +29 -0
  304. package/studio/web/lib/mic-device.ts +72 -0
  305. package/studio/web/lib/missing-dms.ts +70 -0
  306. package/studio/web/lib/omni-cards.ts +42 -0
  307. package/studio/web/lib/omni-cascade.ts +1067 -0
  308. package/studio/web/lib/omni-greeting-audio.ts +24 -0
  309. package/studio/web/lib/omni-hotword.ts +231 -0
  310. package/studio/web/lib/omni-panel.ts +67 -0
  311. package/studio/web/lib/omni-realtime.ts +811 -0
  312. package/studio/web/lib/omni-ui.ts +402 -0
  313. package/studio/web/lib/omni-wait.ts +174 -0
  314. package/studio/web/lib/omnichat-api.ts +44 -0
  315. package/studio/web/lib/orb.ts +270 -0
  316. package/studio/web/lib/pcm-recorder.ts +115 -0
  317. package/studio/web/lib/platforms.ts +17 -0
  318. package/studio/web/lib/providers.ts +27 -0
  319. package/studio/web/lib/publish-draft.ts +52 -0
  320. package/studio/web/lib/publish-settings.ts +81 -0
  321. package/studio/web/lib/query-client.ts +122 -0
  322. package/studio/web/lib/references.ts +13 -0
  323. package/studio/web/lib/services.ts +40 -0
  324. package/studio/web/lib/sessions.ts +103 -0
  325. package/studio/web/lib/settings-categories.ts +58 -0
  326. package/studio/web/lib/sound-audio.ts +33 -0
  327. package/studio/web/lib/sound-timeline.ts +291 -0
  328. package/studio/web/lib/sse.ts +86 -0
  329. package/studio/web/lib/stage-history.ts +141 -0
  330. package/studio/web/lib/stages.ts +344 -0
  331. package/studio/web/lib/studio-offline.ts +60 -0
  332. package/studio/web/lib/upload.ts +24 -0
  333. package/studio/web/lib/video-tabs.ts +118 -0
  334. package/studio/web/lib/voice-profile-draft.ts +34 -0
  335. package/studio/web/lib/voice-profiles.ts +3 -0
  336. package/studio/web/main.tsx +14 -0
  337. package/studio/web/pages/AdminPage/index.tsx +123 -0
  338. package/studio/web/pages/AgentPage/components/Measurements.tsx +102 -0
  339. package/studio/web/pages/AgentPage/index.tsx +28 -0
  340. package/studio/web/pages/AnalyticsPage/components/About.tsx +52 -0
  341. package/studio/web/pages/AnalyticsPage/components/ChannelCards.tsx +106 -0
  342. package/studio/web/pages/AnalyticsPage/components/ChannelView.tsx +137 -0
  343. package/studio/web/pages/AnalyticsPage/components/CollectionStatus.tsx +46 -0
  344. package/studio/web/pages/AnalyticsPage/components/Comparison.tsx +68 -0
  345. package/studio/web/pages/AnalyticsPage/components/Diagnosis.tsx +50 -0
  346. package/studio/web/pages/AnalyticsPage/components/Highlights.tsx +49 -0
  347. package/studio/web/pages/AnalyticsPage/components/Hint.tsx +11 -0
  348. package/studio/web/pages/AnalyticsPage/components/HookVsBody.tsx +100 -0
  349. package/studio/web/pages/AnalyticsPage/components/HourlyWave.tsx +133 -0
  350. package/studio/web/pages/AnalyticsPage/components/Insights.tsx +70 -0
  351. package/studio/web/pages/AnalyticsPage/components/MetricCard.tsx +46 -0
  352. package/studio/web/pages/AnalyticsPage/components/PlanSection.tsx +40 -0
  353. package/studio/web/pages/AnalyticsPage/components/ProgressionChart.tsx +155 -0
  354. package/studio/web/pages/AnalyticsPage/components/ReelBarsChart.tsx +154 -0
  355. package/studio/web/pages/AnalyticsPage/components/ReelThumb.tsx +19 -0
  356. package/studio/web/pages/AnalyticsPage/components/ReelView.tsx +290 -0
  357. package/studio/web/pages/AnalyticsPage/components/ReelsTable.tsx +269 -0
  358. package/studio/web/pages/AnalyticsPage/components/RequestWithFocus.tsx +38 -0
  359. package/studio/web/pages/AnalyticsPage/components/SegmentedControl.tsx +35 -0
  360. package/studio/web/pages/AnalyticsPage/components/VsMedian.tsx +31 -0
  361. package/studio/web/pages/AnalyticsPage/index.tsx +17 -0
  362. package/studio/web/pages/GettingStartedPage/index.tsx +174 -0
  363. package/studio/web/pages/NewVideoPage/components/DeliverablesPicker.tsx +62 -0
  364. package/studio/web/pages/NewVideoPage/components/FormatPicker.tsx +33 -0
  365. package/studio/web/pages/NewVideoPage/index.tsx +133 -0
  366. package/studio/web/pages/NotFoundPage/index.tsx +9 -0
  367. package/studio/web/pages/OmnichatPage/components/NewFromLink.tsx +59 -0
  368. package/studio/web/pages/OmnichatPage/components/RuleListItem.tsx +64 -0
  369. package/studio/web/pages/OmnichatPage/components/RulePreview.tsx +14 -0
  370. package/studio/web/pages/OmnichatPage/components/RuleView.tsx +88 -0
  371. package/studio/web/pages/OmnichatPage/index.tsx +153 -0
  372. package/studio/web/pages/QueuePage/index.tsx +22 -0
  373. package/studio/web/pages/ReferencePage/components/ReferenceTexts.tsx +131 -0
  374. package/studio/web/pages/ReferencePage/index.tsx +216 -0
  375. package/studio/web/pages/ReferencesPage/components/NewReferenceForm.tsx +150 -0
  376. package/studio/web/pages/ReferencesPage/components/ReferenceCard.tsx +42 -0
  377. package/studio/web/pages/ReferencesPage/components/ReferenceGridSkeleton.tsx +31 -0
  378. package/studio/web/pages/ReferencesPage/index.tsx +67 -0
  379. package/studio/web/pages/SchedulePage/components/CalendarSkeleton.tsx +18 -0
  380. package/studio/web/pages/SchedulePage/components/DayView.tsx +177 -0
  381. package/studio/web/pages/SchedulePage/components/HourPicker.tsx +103 -0
  382. package/studio/web/pages/SchedulePage/components/MonthDayCell.tsx +111 -0
  383. package/studio/web/pages/SchedulePage/components/MonthView.tsx +47 -0
  384. package/studio/web/pages/SchedulePage/components/PlatformIcons.tsx +12 -0
  385. package/studio/web/pages/SchedulePage/components/PostThumb.tsx +15 -0
  386. package/studio/web/pages/SchedulePage/components/WeekColumn.tsx +75 -0
  387. package/studio/web/pages/SchedulePage/components/WeekView.tsx +35 -0
  388. package/studio/web/pages/SchedulePage/index.tsx +285 -0
  389. package/studio/web/pages/SettingsPage/components/AgentAccounts.tsx +263 -0
  390. package/studio/web/pages/SettingsPage/components/AgentCategory.tsx +20 -0
  391. package/studio/web/pages/SettingsPage/components/ModelPicker.tsx +49 -0
  392. package/studio/web/pages/SettingsPage/components/ModelsSettings.tsx +138 -0
  393. package/studio/web/pages/SettingsPage/components/PlatformsCategory.tsx +23 -0
  394. package/studio/web/pages/SettingsPage/components/PublishingCategory/AccountStatus.tsx +29 -0
  395. package/studio/web/pages/SettingsPage/components/PublishingCategory/AutoPublishPanel.tsx +90 -0
  396. package/studio/web/pages/SettingsPage/components/PublishingCategory/BrowserConnect.tsx +62 -0
  397. package/studio/web/pages/SettingsPage/components/PublishingCategory/CheckField.tsx +19 -0
  398. package/studio/web/pages/SettingsPage/components/PublishingCategory/InstagramPanel.tsx +113 -0
  399. package/studio/web/pages/SettingsPage/components/PublishingCategory/OmnichatSettingsPanel.tsx +146 -0
  400. package/studio/web/pages/SettingsPage/components/PublishingCategory/SaveBar.tsx +33 -0
  401. package/studio/web/pages/SettingsPage/components/PublishingCategory/SchedulePanel.tsx +132 -0
  402. package/studio/web/pages/SettingsPage/components/PublishingCategory/SettingsCard.tsx +47 -0
  403. package/studio/web/pages/SettingsPage/components/PublishingCategory/SettingsCardGroup.tsx +19 -0
  404. package/studio/web/pages/SettingsPage/components/PublishingCategory/TextPanel.tsx +55 -0
  405. package/studio/web/pages/SettingsPage/components/PublishingCategory/TiktokPanel.tsx +123 -0
  406. package/studio/web/pages/SettingsPage/components/PublishingCategory/YoutubePanel.tsx +100 -0
  407. package/studio/web/pages/SettingsPage/components/PublishingCategory/badges.tsx +15 -0
  408. package/studio/web/pages/SettingsPage/components/PublishingCategory/index.tsx +40 -0
  409. package/studio/web/pages/SettingsPage/components/PublishingCategory/types.ts +10 -0
  410. package/studio/web/pages/SettingsPage/components/UsageCategory.tsx +389 -0
  411. package/studio/web/pages/SettingsPage/components/VoiceProvidersCategory.tsx +97 -0
  412. package/studio/web/pages/SettingsPage/index.tsx +85 -0
  413. package/studio/web/pages/VideoPage/components/AudioPlayer.tsx +113 -0
  414. package/studio/web/pages/VideoPage/components/CaptionPositionEditor/AxisField.tsx +50 -0
  415. package/studio/web/pages/VideoPage/components/CaptionPositionEditor/CaptionList.tsx +56 -0
  416. package/studio/web/pages/VideoPage/components/CaptionPositionEditor/CaptionListItem.tsx +92 -0
  417. package/studio/web/pages/VideoPage/components/CaptionPositionEditor/CaptionMargins.tsx +57 -0
  418. package/studio/web/pages/VideoPage/components/CaptionPositionEditor/CaptionStyleFields.tsx +70 -0
  419. package/studio/web/pages/VideoPage/components/CaptionPositionEditor/CaptionTextForm.tsx +87 -0
  420. package/studio/web/pages/VideoPage/components/CaptionPositionEditor/ColorField.tsx +37 -0
  421. package/studio/web/pages/VideoPage/components/CaptionPositionEditor/MarginField.tsx +59 -0
  422. package/studio/web/pages/VideoPage/components/CaptionPositionEditor/NumberField.tsx +57 -0
  423. package/studio/web/pages/VideoPage/components/CaptionPositionEditor/PositionControls.tsx +140 -0
  424. package/studio/web/pages/VideoPage/components/CaptionPositionEditor/captionEditor.ts +215 -0
  425. package/studio/web/pages/VideoPage/components/CaptionPositionEditor/useCaptionPosition.ts +154 -0
  426. package/studio/web/pages/VideoPage/components/CarouselChangeRequest.tsx +72 -0
  427. package/studio/web/pages/VideoPage/components/CarouselCompositionStage.tsx +80 -0
  428. package/studio/web/pages/VideoPage/components/CarouselCreate.tsx +63 -0
  429. package/studio/web/pages/VideoPage/components/CarouselMissing.tsx +19 -0
  430. package/studio/web/pages/VideoPage/components/CarouselPublishStage.tsx +173 -0
  431. package/studio/web/pages/VideoPage/components/CarouselScriptStage.tsx +82 -0
  432. package/studio/web/pages/VideoPage/components/CarouselSlideCard.tsx +49 -0
  433. package/studio/web/pages/VideoPage/components/CarouselViewer.tsx +55 -0
  434. package/studio/web/pages/VideoPage/components/ChangedVoices.tsx +53 -0
  435. package/studio/web/pages/VideoPage/components/CompositionStage.tsx +78 -0
  436. package/studio/web/pages/VideoPage/components/CoverPicker.tsx +32 -0
  437. package/studio/web/pages/VideoPage/components/EditorStage.tsx +53 -0
  438. package/studio/web/pages/VideoPage/components/LineTakes.tsx +160 -0
  439. package/studio/web/pages/VideoPage/components/LineTextEditor.tsx +124 -0
  440. package/studio/web/pages/VideoPage/components/LineVoicePicker.tsx +37 -0
  441. package/studio/web/pages/VideoPage/components/MediaImage.tsx +25 -0
  442. package/studio/web/pages/VideoPage/components/MotionScriptStage.tsx +39 -0
  443. package/studio/web/pages/VideoPage/components/NarrationStage.tsx +70 -0
  444. package/studio/web/pages/VideoPage/components/PublishForm.tsx +387 -0
  445. package/studio/web/pages/VideoPage/components/PublishStage.tsx +76 -0
  446. package/studio/web/pages/VideoPage/components/PublishText.tsx +149 -0
  447. package/studio/web/pages/VideoPage/components/ScenesApprovalPanel.tsx +278 -0
  448. package/studio/web/pages/VideoPage/components/ScheduledInfo.tsx +72 -0
  449. package/studio/web/pages/VideoPage/components/ScriptApprovalPanel.tsx +189 -0
  450. package/studio/web/pages/VideoPage/components/ScriptStage.tsx +17 -0
  451. package/studio/web/pages/VideoPage/components/SequencePlayer.tsx +146 -0
  452. package/studio/web/pages/VideoPage/components/SoundTimeline/AddSound.tsx +117 -0
  453. package/studio/web/pages/VideoPage/components/SoundTimeline/AreaSelection.tsx +74 -0
  454. package/studio/web/pages/VideoPage/components/SoundTimeline/BedPanel.tsx +94 -0
  455. package/studio/web/pages/VideoPage/components/SoundTimeline/BedRow.tsx +45 -0
  456. package/studio/web/pages/VideoPage/components/SoundTimeline/CaptionOverlay.tsx +44 -0
  457. package/studio/web/pages/VideoPage/components/SoundTimeline/CaptionSaveBar.tsx +32 -0
  458. package/studio/web/pages/VideoPage/components/SoundTimeline/CaptionsPanel.tsx +22 -0
  459. package/studio/web/pages/VideoPage/components/SoundTimeline/ClipBlock.tsx +111 -0
  460. package/studio/web/pages/VideoPage/components/SoundTimeline/ClipPanel.tsx +76 -0
  461. package/studio/web/pages/VideoPage/components/SoundTimeline/CutsPanel.tsx +44 -0
  462. package/studio/web/pages/VideoPage/components/SoundTimeline/EditorTabBar.tsx +73 -0
  463. package/studio/web/pages/VideoPage/components/SoundTimeline/EffectChoice.tsx +46 -0
  464. package/studio/web/pages/VideoPage/components/SoundTimeline/EffectItem.tsx +89 -0
  465. package/studio/web/pages/VideoPage/components/SoundTimeline/EffectPanel.tsx +232 -0
  466. package/studio/web/pages/VideoPage/components/SoundTimeline/EffectsRow.tsx +21 -0
  467. package/studio/web/pages/VideoPage/components/SoundTimeline/Lane.tsx +39 -0
  468. package/studio/web/pages/VideoPage/components/SoundTimeline/MarkerRow.tsx +40 -0
  469. package/studio/web/pages/VideoPage/components/SoundTimeline/MuteButton.tsx +36 -0
  470. package/studio/web/pages/VideoPage/components/SoundTimeline/NumberInput.tsx +37 -0
  471. package/studio/web/pages/VideoPage/components/SoundTimeline/PlanWarnings.tsx +19 -0
  472. package/studio/web/pages/VideoPage/components/SoundTimeline/PlayerResizeHandle.tsx +50 -0
  473. package/studio/web/pages/VideoPage/components/SoundTimeline/PreviewControls.tsx +51 -0
  474. package/studio/web/pages/VideoPage/components/SoundTimeline/SceneRow.tsx +54 -0
  475. package/studio/web/pages/VideoPage/components/SoundTimeline/SettingsPanel.tsx +65 -0
  476. package/studio/web/pages/VideoPage/components/SoundTimeline/SoundLibrary.tsx +99 -0
  477. package/studio/web/pages/VideoPage/components/SoundTimeline/SoundPreview.tsx +82 -0
  478. package/studio/web/pages/VideoPage/components/SoundTimeline/SpeedPanel.tsx +128 -0
  479. package/studio/web/pages/VideoPage/components/SoundTimeline/TimelineDock.tsx +56 -0
  480. package/studio/web/pages/VideoPage/components/SoundTimeline/TimelineFooter.tsx +26 -0
  481. package/studio/web/pages/VideoPage/components/SoundTimeline/TimelineSkeleton.tsx +21 -0
  482. package/studio/web/pages/VideoPage/components/SoundTimeline/TimelineToolbar.tsx +160 -0
  483. package/studio/web/pages/VideoPage/components/SoundTimeline/TimelineTracks.tsx +121 -0
  484. package/studio/web/pages/VideoPage/components/SoundTimeline/TrackSidebar.tsx +30 -0
  485. package/studio/web/pages/VideoPage/components/SoundTimeline/VoiceRow.tsx +53 -0
  486. package/studio/web/pages/VideoPage/components/SoundTimeline/Waveform.tsx +97 -0
  487. package/studio/web/pages/VideoPage/components/SoundTimeline/clipActions.ts +50 -0
  488. package/studio/web/pages/VideoPage/components/SoundTimeline/effectActions.ts +161 -0
  489. package/studio/web/pages/VideoPage/components/SoundTimeline/index.tsx +161 -0
  490. package/studio/web/pages/VideoPage/components/SoundTimeline/timelineGeometry.ts +45 -0
  491. package/studio/web/pages/VideoPage/components/SoundTimeline/types.ts +43 -0
  492. package/studio/web/pages/VideoPage/components/SoundTimeline/useAutosave.ts +107 -0
  493. package/studio/web/pages/VideoPage/components/SoundTimeline/useEditorShortcuts.ts +168 -0
  494. package/studio/web/pages/VideoPage/components/SoundTimeline/useFitHeight.ts +40 -0
  495. package/studio/web/pages/VideoPage/components/SoundTimeline/useFollowPlayhead.ts +21 -0
  496. package/studio/web/pages/VideoPage/components/SoundTimeline/useListenMix.ts +43 -0
  497. package/studio/web/pages/VideoPage/components/SoundTimeline/usePlayback.ts +231 -0
  498. package/studio/web/pages/VideoPage/components/SoundTimeline/usePlayerWidth.ts +27 -0
  499. package/studio/web/pages/VideoPage/components/SoundTimeline/useSoundData.ts +124 -0
  500. package/studio/web/pages/VideoPage/components/SoundTimeline/useSoundTimeline.ts +511 -0
  501. package/studio/web/pages/VideoPage/components/StageDrawer/CompareVersions.tsx +111 -0
  502. package/studio/web/pages/VideoPage/components/StageDrawer/VersionCard.tsx +81 -0
  503. package/studio/web/pages/VideoPage/components/StageDrawer/VersionMedia.tsx +24 -0
  504. package/studio/web/pages/VideoPage/components/StageDrawer/VersionText.tsx +15 -0
  505. package/studio/web/pages/VideoPage/components/StageDrawer/index.tsx +109 -0
  506. package/studio/web/pages/VideoPage/components/StageProcess.tsx +126 -0
  507. package/studio/web/pages/VideoPage/components/StageResult.tsx +69 -0
  508. package/studio/web/pages/VideoPage/components/SwapReference.tsx +92 -0
  509. package/studio/web/pages/VideoPage/components/ThumbnailPanel.tsx +252 -0
  510. package/studio/web/pages/VideoPage/components/TimingPanel.tsx +89 -0
  511. package/studio/web/pages/VideoPage/components/VideoPlayer.tsx +18 -0
  512. package/studio/web/pages/VideoPage/components/VideoSkeleton.tsx +50 -0
  513. package/studio/web/pages/VideoPage/components/VideoVoiceInfo.tsx +33 -0
  514. package/studio/web/pages/VideoPage/components/VideoVoicePicker.tsx +165 -0
  515. package/studio/web/pages/VideoPage/components/VoicePauseBanner.tsx +56 -0
  516. package/studio/web/pages/VideoPage/components/WithLogo.tsx +10 -0
  517. package/studio/web/pages/VideoPage/index.tsx +295 -0
  518. package/studio/web/pages/VideosPage/components/PublishedOn.tsx +85 -0
  519. package/studio/web/pages/VideosPage/components/ServicesPanel.tsx +81 -0
  520. package/studio/web/pages/VideosPage/components/SessionsPanel.tsx +62 -0
  521. package/studio/web/pages/VideosPage/components/VideoListSkeleton.tsx +25 -0
  522. package/studio/web/pages/VideosPage/components/VideoRow.tsx +122 -0
  523. package/studio/web/pages/VideosPage/index.tsx +134 -0
  524. package/studio/web/pages/VoicePage/index.tsx +28 -0
  525. package/studio/web/router.tsx +201 -0
  526. package/studio/web/styles/base/elements.css +55 -0
  527. package/studio/web/styles/base/utilities.css +25 -0
  528. package/studio/web/styles/components/badge.css +33 -0
  529. package/studio/web/styles/components/claude-running.css +27 -0
  530. package/studio/web/styles/components/claude-session.css +212 -0
  531. package/studio/web/styles/components/claude-walking.css +92 -0
  532. package/studio/web/styles/components/clone-voice-save.css +136 -0
  533. package/studio/web/styles/components/clone-voice.css +379 -0
  534. package/studio/web/styles/components/controls.css +70 -0
  535. package/studio/web/styles/components/data-and-image.css +54 -0
  536. package/studio/web/styles/components/forms.css +132 -0
  537. package/studio/web/styles/components/human-banner.css +99 -0
  538. package/studio/web/styles/components/lightbox.css +50 -0
  539. package/studio/web/styles/components/markdown.css +68 -0
  540. package/studio/web/styles/components/modal.css +70 -0
  541. package/studio/web/styles/components/omni-cards.css +105 -0
  542. package/studio/web/styles/components/omni.css +859 -0
  543. package/studio/web/styles/components/omnichat.css +538 -0
  544. package/studio/web/styles/components/panel.css +115 -0
  545. package/studio/web/styles/components/platform-card.css +140 -0
  546. package/studio/web/styles/components/provider-card.css +132 -0
  547. package/studio/web/styles/components/queue.css +185 -0
  548. package/studio/web/styles/components/reference-actions.css +133 -0
  549. package/studio/web/styles/components/reference-picker.css +146 -0
  550. package/studio/web/styles/components/results-drawer.css +224 -0
  551. package/studio/web/styles/components/segmented.css +115 -0
  552. package/studio/web/styles/components/side-panel-tab.css +23 -0
  553. package/studio/web/styles/components/status-signals.css +66 -0
  554. package/studio/web/styles/components/stepper.css +208 -0
  555. package/studio/web/styles/components/table.css +19 -0
  556. package/studio/web/styles/components/tabs.css +24 -0
  557. package/studio/web/styles/components/ui/accordion.css +71 -0
  558. package/studio/web/styles/components/ui/chart.css +162 -0
  559. package/studio/web/styles/components/ui/checkbox.css +37 -0
  560. package/studio/web/styles/components/ui/date-picker.css +153 -0
  561. package/studio/web/styles/components/ui/select.css +110 -0
  562. package/studio/web/styles/components/ui/skeleton.css +25 -0
  563. package/studio/web/styles/components/ui/slider.css +81 -0
  564. package/studio/web/styles/components/ui/switch.css +41 -0
  565. package/studio/web/styles/components/ui/tooltip.css +24 -0
  566. package/studio/web/styles/components/voice-carousel.css +255 -0
  567. package/studio/web/styles/components/voice-editor.css +179 -0
  568. package/studio/web/styles/components/voice-grid.css +56 -0
  569. package/studio/web/styles/components/voice-profiles.css +192 -0
  570. package/studio/web/styles/design-system.md +136 -0
  571. package/studio/web/styles/index.css +116 -0
  572. package/studio/web/styles/layout/page.css +41 -0
  573. package/studio/web/styles/layout/sidebar-groups.css +39 -0
  574. package/studio/web/styles/layout/sidebar.css +111 -0
  575. package/studio/web/styles/pages/agent.css +26 -0
  576. package/studio/web/styles/pages/analytics/dashboard.css +614 -0
  577. package/studio/web/styles/pages/analytics/insights.css +48 -0
  578. package/studio/web/styles/pages/getting-started.css +166 -0
  579. package/studio/web/styles/pages/new-video/form.css +32 -0
  580. package/studio/web/styles/pages/not-found.css +59 -0
  581. package/studio/web/styles/pages/references.css +204 -0
  582. package/studio/web/styles/pages/schedule/calendar.css +541 -0
  583. package/studio/web/styles/pages/schedule/chip-states.css +60 -0
  584. package/studio/web/styles/pages/settings/models.css +72 -0
  585. package/studio/web/styles/pages/settings/shell.css +361 -0
  586. package/studio/web/styles/pages/settings/usage.css +63 -0
  587. package/studio/web/styles/pages/studio-offline.css +105 -0
  588. package/studio/web/styles/pages/video/caption-controls.css +194 -0
  589. package/studio/web/styles/pages/video/captions-grid.css +70 -0
  590. package/studio/web/styles/pages/video/captions.css +79 -0
  591. package/studio/web/styles/pages/video/editor-chat.css +103 -0
  592. package/studio/web/styles/pages/video/export.css +50 -0
  593. package/studio/web/styles/pages/video/frames.css +29 -0
  594. package/studio/web/styles/pages/video/fullscreen-stage.css +400 -0
  595. package/studio/web/styles/pages/video/instagram-carousel.css +309 -0
  596. package/studio/web/styles/pages/video/layout.css +285 -0
  597. package/studio/web/styles/pages/video/narration.css +138 -0
  598. package/studio/web/styles/pages/video/preview-overview.css +11 -0
  599. package/studio/web/styles/pages/video/processing-notice.css +34 -0
  600. package/studio/web/styles/pages/video/publish-when.css +76 -0
  601. package/studio/web/styles/pages/video/publish.css +97 -0
  602. package/studio/web/styles/pages/video/result.css +96 -0
  603. package/studio/web/styles/pages/video/scenes.css +167 -0
  604. package/studio/web/styles/pages/video/sound-timeline.css +672 -0
  605. package/studio/web/styles/pages/video/sound-toolbar.css +149 -0
  606. package/studio/web/styles/pages/video/take-player.css +100 -0
  607. package/studio/web/styles/pages/video/thumbnail.css +157 -0
  608. package/studio/web/styles/pages/video/timeline.css +117 -0
  609. package/studio/web/styles/pages/video/voice-picker.css +14 -0
  610. package/studio/web/styles/pages/video-list/active-row.css +27 -0
  611. package/studio/web/styles/pages/video-list/card-link.css +49 -0
  612. package/studio/web/styles/pages/video-list/list.css +86 -0
  613. package/studio/web/styles/theme/identity/details.css +204 -0
  614. package/studio/web/styles/theme/identity/shell.css +249 -0
  615. package/studio/web/styles/theme/identity/surfaces.css +183 -0
  616. package/studio/web/styles/theme/identity/titles-and-buttons.css +120 -0
  617. package/studio/web/styles/theme/no-borders/blocks.css +208 -0
  618. package/studio/web/styles/theme/no-borders/controls.css +256 -0
  619. package/studio/web/styles/tokens.css +245 -0
  620. package/studio/web/types/api.ts +2 -0
  621. package/studio/web/types/assets.d.ts +9 -0
  622. package/templates/brief.md +27 -0
  623. package/templates/captions.html +204 -0
  624. package/templates/carousel-composition.md +47 -0
  625. package/templates/carousel-script.md +60 -0
  626. package/templates/components/voiceavatar/README.md +87 -0
  627. package/templates/components/voiceavatar/demo.html +50 -0
  628. package/templates/components/voiceavatar/voiceavatar.js +415 -0
  629. package/templates/motion.md +80 -0
  630. package/templates/playbook.md +43 -0
  631. package/templates/reference.md +123 -0
  632. package/templates/scene.html +112 -0
  633. package/templates/scenes.example.json +96 -0
  634. package/templates/script.md +63 -0
  635. package/templates/slide.html +71 -0
  636. package/templates/wireframe.html +91 -0
  637. package/tools/analysis.mjs +300 -0
  638. package/tools/approval.mjs +442 -0
  639. package/tools/assemble.mjs +942 -0
  640. package/tools/build-motion.mjs +437 -0
  641. package/tools/captions.mjs +246 -0
  642. package/tools/carousel.mjs +558 -0
  643. package/tools/compose.mjs +307 -0
  644. package/tools/cuts.mjs +74 -0
  645. package/tools/editor.mjs +419 -0
  646. package/tools/epidemic-capture.js +54 -0
  647. package/tools/fanout.mjs +69 -0
  648. package/tools/history.mjs +70 -0
  649. package/tools/history.py +61 -0
  650. package/tools/job-queue.mjs +180 -0
  651. package/tools/job_queue.py +430 -0
  652. package/tools/lib/agents.mjs +412 -0
  653. package/tools/lib/channel.mjs +399 -0
  654. package/tools/lib/claude-settings.mjs +67 -0
  655. package/tools/lib/cli.mjs +9 -0
  656. package/tools/lib/fanout.mjs +234 -0
  657. package/tools/lib/http.mjs +94 -0
  658. package/tools/lib/index.js +14 -0
  659. package/tools/lib/mcp-server.mjs +69 -0
  660. package/tools/lib/metrics/metrics.mjs +204 -0
  661. package/tools/lib/narration.mjs +15 -0
  662. package/tools/lib/oauth-client.mjs +295 -0
  663. package/tools/lib/omnichat/config.mjs +139 -0
  664. package/tools/lib/omnichat/errors.mjs +42 -0
  665. package/tools/lib/omnichat/extract.mjs +82 -0
  666. package/tools/lib/omnichat/gate.mjs +151 -0
  667. package/tools/lib/omnichat/manual.mjs +166 -0
  668. package/tools/lib/omnichat/match.mjs +78 -0
  669. package/tools/lib/omnichat/pass.mjs +393 -0
  670. package/tools/lib/omnichat/record.mjs +208 -0
  671. package/tools/lib/omnichat/text.mjs +315 -0
  672. package/tools/lib/omnichat/webhook.mjs +166 -0
  673. package/tools/lib/pairing.mjs +99 -0
  674. package/tools/lib/paths.mjs +41 -0
  675. package/tools/lib/platforms/instagram.mjs +469 -0
  676. package/tools/lib/platforms/tiktok.mjs +343 -0
  677. package/tools/lib/platforms/youtube.mjs +228 -0
  678. package/tools/lib/publish/publish.mjs +244 -0
  679. package/tools/lib/schedule/schedule.mjs +191 -0
  680. package/tools/lib/service.mjs +134 -0
  681. package/tools/lib/skill-client.mjs +126 -0
  682. package/tools/lib/skills-release.mjs +196 -0
  683. package/tools/lib/testing.mjs +52 -0
  684. package/tools/lib/up.mjs +187 -0
  685. package/tools/metrics.mjs +415 -0
  686. package/tools/models.mjs +134 -0
  687. package/tools/narrate.mjs +257 -0
  688. package/tools/omni.mjs +298 -0
  689. package/tools/omni_paths.py +12 -0
  690. package/tools/omnichat.mjs +1577 -0
  691. package/tools/omnivideo.py +1122 -0
  692. package/tools/package.mjs +130 -0
  693. package/tools/platforms.mjs +236 -0
  694. package/tools/premiere.mjs +533 -0
  695. package/tools/publish.mjs +1496 -0
  696. package/tools/reference.mjs +1408 -0
  697. package/tools/render.mjs +426 -0
  698. package/tools/runner.mjs +156 -0
  699. package/tools/setup.mjs +466 -0
  700. package/tools/skills-release.mjs +93 -0
  701. package/tools/sound.mjs +1323 -0
  702. package/tools/soundtrack.mjs +217 -0
  703. package/tools/thumbnail.mjs +271 -0
  704. package/tools/timing.mjs +108 -0
  705. package/tools/usage.py +628 -0
  706. package/tools/voice-envelope.mjs +185 -0
  707. package/tools/voice.py +448 -0
  708. package/tools/voice_sample.py +75 -0
  709. package/tools/wireframe.mjs +230 -0
@@ -0,0 +1,295 @@
1
+ /* The CLI side of the studio's OAuth login (cloud/studio/src/oauth.mjs; docs/specs/phase-2-plugin-cli.md,
2
+ step 2): PKCE, the loopback callback, the token exchange, and the tokens in the macOS Keychain, refreshed
3
+ under a lock.
4
+
5
+ login(studioUrl, …) opens the approval page in the browser, waits on http://127.0.0.1:<port>/callback,
6
+ trades the code and stores the tokens
7
+ accessToken(…) a valid access token, refreshing it when it has under a minute left
8
+ logout(…) signs this machine out in the cloud and forgets the tokens
9
+
10
+ Two processes (the studio's runner and a CLI command) may refresh at once. The server treats a rotated
11
+ refresh token used twice as a leak and signs the machine out, so a refresh holds a lock and rereads the
12
+ Keychain inside it: the second process finds the tokens the first one just stored.
13
+
14
+ The Keychain item: service "omni", account = the studio's host, the password a JSON with the tokens.
15
+ `security` (and the lock dir, the clock, fetch, the browser) are injected for the tests. Messages in
16
+ Portuguese: they reach the user as they are. */
17
+
18
+ import { spawnSync } from "node:child_process";
19
+ import { createHash, randomBytes } from "node:crypto";
20
+ import { mkdirSync, rmSync, statSync } from "node:fs";
21
+ import { createServer } from "node:http";
22
+ import { homedir } from "node:os";
23
+ import { join } from "node:path";
24
+
25
+ export const CLI_CLIENT = "omnivideo-cli";
26
+ /* an access token's prefix (cloud/studio/src/oauth.mjs): tells it from a Phase 1 key (`ovr_`) */
27
+ export const ACCESS_PREFIX = "ova_";
28
+ export const KEYCHAIN_SERVICE = "omni";
29
+ const REFRESH_MARGIN_MS = 60_000;
30
+ const LOGIN_TIMEOUT_MS = 10 * 60_000;
31
+ const LOCK_STALE_MS = 30_000;
32
+
33
+ const b64url = (/** @type {Buffer} */ b) => b.toString("base64url");
34
+
35
+ /** a PKCE verifier and its S256 challenge (RFC 7636) */
36
+ export function pkce() {
37
+ const verifier = b64url(randomBytes(48));
38
+ return { verifier, challenge: b64url(createHash("sha256").update(verifier).digest()) };
39
+ }
40
+
41
+ /**
42
+ * @typedef {{ access_token: string, refresh_token: string, expires_at: number, device_id: string, device_name: string }} Tokens
43
+ * @typedef {{
44
+ * fetch?: typeof fetch,
45
+ * now?: () => number,
46
+ * security?: string,
47
+ * lockDir?: string,
48
+ * open?: (url: string) => void,
49
+ * log?: (line: string) => void,
50
+ * }} Deps
51
+ */
52
+
53
+ const hostOf = (/** @type {string} */ studioUrl) => new URL(studioUrl).host;
54
+
55
+ /* --- the Keychain ------------------------------------------------------------------------ */
56
+
57
+ /** @param {string} studioUrl @param {Deps} [d] @returns {Tokens | null} */
58
+ export function readTokens(studioUrl, d = {}) {
59
+ const r = spawnSync(
60
+ d.security ?? "security",
61
+ ["find-generic-password", "-s", KEYCHAIN_SERVICE, "-a", hostOf(studioUrl), "-w"],
62
+ { encoding: "utf8" },
63
+ );
64
+ if (r.status !== 0) return null;
65
+ try {
66
+ const t = JSON.parse(r.stdout.trim());
67
+ return t?.access_token && t?.refresh_token ? t : null;
68
+ } catch {
69
+ return null;
70
+ }
71
+ }
72
+
73
+ /** @param {string} studioUrl @param {Tokens} tokens @param {Deps} [d] */
74
+ export function writeTokens(studioUrl, tokens, d = {}) {
75
+ /* -U updates the item in place; the secret goes as an argument, which `security` has no other way to take */
76
+ const r = spawnSync(
77
+ d.security ?? "security",
78
+ [
79
+ "add-generic-password",
80
+ "-U",
81
+ "-s",
82
+ KEYCHAIN_SERVICE,
83
+ "-a",
84
+ hostOf(studioUrl),
85
+ "-l",
86
+ "OmniVideo",
87
+ "-w",
88
+ JSON.stringify(tokens),
89
+ ],
90
+ { encoding: "utf8" },
91
+ );
92
+ if (r.status !== 0)
93
+ throw new Error(`não consegui gravar o login no Keychain: ${r.stderr.trim() || `código ${r.status}`}`);
94
+ }
95
+
96
+ /** @param {string} studioUrl @param {Deps} [d] */
97
+ export function forgetTokens(studioUrl, d = {}) {
98
+ spawnSync(d.security ?? "security", ["delete-generic-password", "-s", KEYCHAIN_SERVICE, "-a", hostOf(studioUrl)], {
99
+ encoding: "utf8",
100
+ });
101
+ }
102
+
103
+ /* --- the token endpoint ------------------------------------------------------------------ */
104
+
105
+ /** @param {string} studioUrl @param {Record<string, string>} fields @param {Deps} d */
106
+ async function tokenCall(studioUrl, fields, d) {
107
+ const r = await (d.fetch ?? fetch)(`${studioUrl}/oauth/token`, {
108
+ method: "POST",
109
+ headers: { "content-type": "application/x-www-form-urlencoded" },
110
+ body: new URLSearchParams({ client_id: CLI_CLIENT, ...fields }),
111
+ });
112
+ const body = /** @type {any} */ (await r.json().catch(() => ({})));
113
+ return { status: r.status, ok: r.ok, body };
114
+ }
115
+
116
+ /** @param {any} body @param {Deps} d @returns {Tokens} */
117
+ const toTokens = (body, d) => ({
118
+ access_token: body.access_token,
119
+ refresh_token: body.refresh_token,
120
+ expires_at: (d.now ?? Date.now)() + Number(body.expires_in ?? 900) * 1000,
121
+ device_id: body.device_id,
122
+ device_name: body.device_name,
123
+ });
124
+
125
+ /* --- the lock ---------------------------------------------------------------------------- */
126
+
127
+ /** runs `fn` while holding a lock dir (a crashed holder's lock goes stale after 30 s)
128
+ * @template T @param {string} dir @param {() => Promise<T>} fn @returns {Promise<T>} */
129
+ async function withLock(dir, fn) {
130
+ const deadline = Date.now() + 15_000;
131
+ for (;;) {
132
+ try {
133
+ mkdirSync(dir, { recursive: false });
134
+ break;
135
+ } catch (e) {
136
+ if (/** @type {NodeJS.ErrnoException} */ (e).code === "ENOENT") {
137
+ mkdirSync(join(dir, ".."), { recursive: true });
138
+ continue;
139
+ }
140
+ try {
141
+ if (Date.now() - statSync(dir).mtimeMs > LOCK_STALE_MS) rmSync(dir, { recursive: true, force: true });
142
+ } catch {
143
+ /* someone else just released it */
144
+ }
145
+ if (Date.now() > deadline) throw new Error("outro processo está renovando o login; tente de novo");
146
+ await new Promise(r => setTimeout(r, 100));
147
+ }
148
+ }
149
+ try {
150
+ return await fn();
151
+ } finally {
152
+ rmSync(dir, { recursive: true, force: true });
153
+ }
154
+ }
155
+
156
+ const lockDirOf = (/** @type {Deps} */ d) => d.lockDir ?? join(homedir(), ".omni", "refresh.lock");
157
+
158
+ /* --- login, refresh, logout -------------------------------------------------------------- */
159
+
160
+ /**
161
+ * Opens the approval page and waits for the browser to come back with the code.
162
+ * @param {string} studioUrl @param {{ deviceName: string } & Deps} o
163
+ * @returns {Promise<Tokens>}
164
+ */
165
+ export async function login(studioUrl, o) {
166
+ const { verifier, challenge } = pkce();
167
+ const state = b64url(randomBytes(16));
168
+ const log = o.log ?? (() => {});
169
+
170
+ /** @type {(v: { code: string } | { error: string }) => void} */
171
+ let settle = () => {};
172
+ const arrived = new Promise(r => (settle = r));
173
+ const server = createServer((req, res) => {
174
+ const u = new URL(req.url ?? "/", "http://127.0.0.1");
175
+ if (u.pathname !== "/callback") {
176
+ res.writeHead(404).end();
177
+ return;
178
+ }
179
+ const page = (/** @type {string} */ msg) =>
180
+ `<!doctype html><meta charset="utf-8"><title>OmniVideo</title><body style="font:16px system-ui;padding:48px;max-width:32em">${msg}</body>`;
181
+ if (u.searchParams.get("state") !== state) {
182
+ res
183
+ .writeHead(400, { "content-type": "text/html; charset=utf-8" })
184
+ .end(page("Esse retorno não é deste login. Rode o comando de novo."));
185
+ return;
186
+ }
187
+ const code = u.searchParams.get("code");
188
+ const error = u.searchParams.get("error");
189
+ res
190
+ .writeHead(code ? 200 : 400, { "content-type": "text/html; charset=utf-8" })
191
+ .end(
192
+ page(
193
+ code
194
+ ? "Pronto: este computador entrou no OmniVideo. Pode fechar esta aba."
195
+ : "O login foi recusado. Pode fechar esta aba.",
196
+ ),
197
+ );
198
+ settle(code ? { code } : { error: error ?? "recusado" });
199
+ });
200
+ await new Promise((resolve, reject) => {
201
+ server.once("error", reject);
202
+ server.listen(0, "127.0.0.1", () => resolve(undefined));
203
+ });
204
+ try {
205
+ const port = /** @type {import("node:net").AddressInfo} */ (server.address()).port;
206
+ const redirectUri = `http://127.0.0.1:${port}/callback`;
207
+ const url = new URL("/oauth/authorize", studioUrl);
208
+ for (const [k, v] of Object.entries({
209
+ client_id: CLI_CLIENT,
210
+ response_type: "code",
211
+ redirect_uri: redirectUri,
212
+ code_challenge: challenge,
213
+ code_challenge_method: "S256",
214
+ state,
215
+ device_name: o.deviceName,
216
+ }))
217
+ url.searchParams.set(k, v);
218
+ log(`Abrindo o navegador para entrar. Se ele não abrir, use este link:\n ${url}`);
219
+ (o.open ?? openBrowser)(url.toString());
220
+
221
+ const timeout = new Promise(r =>
222
+ setTimeout(() => r({ error: "o login não voltou em 10 minutos" }), LOGIN_TIMEOUT_MS).unref(),
223
+ );
224
+ const result = /** @type {{ code?: string, error?: string }} */ (await Promise.race([arrived, timeout]));
225
+ if (!result.code)
226
+ throw new Error(result.error === "access_denied" ? "o login foi recusado na página" : String(result.error));
227
+
228
+ const t = await tokenCall(
229
+ studioUrl,
230
+ { grant_type: "authorization_code", code: result.code, code_verifier: verifier, redirect_uri: redirectUri },
231
+ o,
232
+ );
233
+ if (!t.ok)
234
+ throw new Error(
235
+ `o studio não entregou o login (${t.status}: ${t.body?.error_description ?? t.body?.error ?? "?"})`,
236
+ );
237
+ const tokens = toTokens(t.body, o);
238
+ writeTokens(studioUrl, tokens, o);
239
+ return tokens;
240
+ } finally {
241
+ server.close();
242
+ }
243
+ }
244
+
245
+ /**
246
+ * A valid access token for this machine, refreshed when needed, or null when it has to log in again
247
+ * (never logged in, signed out in the studio, plan ended). An unreachable studio throws: it says nothing
248
+ * about the login.
249
+ * @param {string} studioUrl @param {Deps} [d] @returns {Promise<string | null>}
250
+ */
251
+ export async function accessToken(studioUrl, d = {}) {
252
+ const now = d.now ?? Date.now;
253
+ const current = readTokens(studioUrl, d);
254
+ if (!current) return null;
255
+ if (current.expires_at - now() > REFRESH_MARGIN_MS) return current.access_token;
256
+ return withLock(lockDirOf(d), async () => {
257
+ /* another process may have refreshed while this one waited */
258
+ const fresh = readTokens(studioUrl, d);
259
+ if (!fresh) return null;
260
+ if (fresh.expires_at - now() > REFRESH_MARGIN_MS) return fresh.access_token;
261
+ const t = await tokenCall(studioUrl, { grant_type: "refresh_token", refresh_token: fresh.refresh_token }, d);
262
+ if (t.ok) {
263
+ const tokens = toTokens(t.body, d);
264
+ writeTokens(studioUrl, tokens, d);
265
+ return tokens.access_token;
266
+ }
267
+ if (t.status === 400 || t.status === 401 || t.status === 403) {
268
+ forgetTokens(studioUrl, d);
269
+ return null;
270
+ }
271
+ throw new Error(`o studio não renovou o login (${t.status}: ${t.body?.error_description ?? t.body?.error ?? "?"})`);
272
+ });
273
+ }
274
+
275
+ /** Signs this machine out in the cloud (when reachable) and forgets the tokens here.
276
+ * @param {string} studioUrl @param {Deps} [d] @returns {Promise<boolean>} whether the cloud confirmed */
277
+ export async function logout(studioUrl, d = {}) {
278
+ const token = await accessToken(studioUrl, d).catch(() => null);
279
+ let confirmed = false;
280
+ if (token) {
281
+ const r = await (d.fetch ?? fetch)(`${studioUrl}/oauth/logout`, {
282
+ method: "POST",
283
+ headers: { authorization: `Bearer ${token}` },
284
+ }).catch(() => null);
285
+ confirmed = !!r?.ok;
286
+ }
287
+ forgetTokens(studioUrl, d);
288
+ return confirmed;
289
+ }
290
+
291
+ /** @param {string} url */
292
+ function openBrowser(url) {
293
+ const cmd = process.platform === "darwin" ? "open" : process.platform === "win32" ? "explorer" : "xdg-open";
294
+ spawnSync(cmd, [url], { stdio: "ignore" });
295
+ }
@@ -0,0 +1,139 @@
1
+ /* omnichat.instagram in the config, with the defaults filled in. Pure. */
2
+
3
+ import { DAY } from "../http.mjs";
4
+
5
+ /**
6
+ * @typedef {object} OmnichatConfig
7
+ * @property {number} max_per_pass
8
+ * @property {number} pause_between_s
9
+ * @property {number} attempts
10
+ * @property {number} rate_limit_pause_min
11
+ * @property {string} default_dm
12
+ * @property {string} default_opening_dm the DM with the button, before the tap
13
+ * @property {string} default_button_label
14
+ * @property {string} default_not_following_dm
15
+ * @property {string} default_retry_label
16
+ * @property {string} default_link_label the button that opens the link (rule.link_button)
17
+ * @property {string} default_skip_label the button that delivers without following (rule.follow_skip)
18
+ * @property {string} default_skip_dm the DM of whoever got the link through that button
19
+ * @property {string[]} default_public_replies
20
+ * @property {string[]} generic_phrases
21
+ * @property {string[]} filler_words
22
+ * @property {string} context_model
23
+ * @property {boolean} enable_on_publish
24
+ * @property {boolean} quote_post
25
+ * @property {{ first_48h: number, first_week: number, after: number }} interval_min
26
+ */
27
+
28
+ export const DEFAULTS = {
29
+ max_per_pass: 15,
30
+ pause_between_s: 2,
31
+ attempts: 3,
32
+ rate_limit_pause_min: 30,
33
+ default_dm: "Oi, @{username}! Aqui está o link que você pediu: {link}",
34
+ default_opening_dm: "Oi, @{username}! Toca no botão aqui embaixo que eu te mando 👇",
35
+ default_button_label: "Quero o link",
36
+ /* an invitation, not a rule: what they gain by following (the link now and the next ones), how to do it
37
+ from the DM (the name at the top opens the profile) and the button to come back */
38
+ default_not_following_dm:
39
+ "Quase lá, @{username}! 🙌 Esse conteúdo eu libero pra quem acompanha o perfil. Toca no meu nome aqui em cima, segue e volta no botão: o link chega na hora, e você ainda fica por dentro dos próximos 🚀",
40
+ default_retry_label: "Pronto, já sigo!",
41
+ default_link_label: "Abrir link",
42
+ default_skip_label: "Receber sem seguir",
43
+ /* the link, thanks, and a soft ask to follow next time (no {link}: it goes after the text, or in the link button) */
44
+ default_skip_dm:
45
+ "@{username}, aqui está o link! Espero que ajude muito.\n\nSe em uma próxima você me seguir, vai ajudar bastante! 😁",
46
+ default_public_replies: [
47
+ "Te mandei no direct 👀",
48
+ "Acabei de te mandar no direct 📩",
49
+ "Corre no direct! 🚀",
50
+ "Já está no seu direct 📬",
51
+ "Mandei no seu direct, dá uma olhada 👀",
52
+ "Olha o direct, @{username}! 📩",
53
+ "Te enviei no direct agora mesmo 🚀",
54
+ "Chegou no seu direct, confere lá 👇",
55
+ "Direct enviado, @{username}! 🙌",
56
+ "Dá uma olhada no direct, mandei por lá 📲",
57
+ "Acabou de cair no seu direct ✅",
58
+ "Mandei lá no direct pra você 🔥",
59
+ ],
60
+ generic_phrases: ["quero", "eu quero", "me manda", "manda", "me envia", "envia", "me passa", "link"],
61
+ filler_words: [
62
+ "sim",
63
+ "por",
64
+ "favor",
65
+ "pfv",
66
+ "pfvr",
67
+ "pf",
68
+ "pls",
69
+ "please",
70
+ "plmds",
71
+ "ai",
72
+ "ae",
73
+ "aqui",
74
+ "o",
75
+ "a",
76
+ "isso",
77
+ "tambem",
78
+ "tbm",
79
+ "tb",
80
+ "eu",
81
+ "me",
82
+ "mim",
83
+ "pra",
84
+ "para",
85
+ "pro",
86
+ "no",
87
+ "na",
88
+ "dm",
89
+ "direct",
90
+ "pv",
91
+ "privado",
92
+ "inbox",
93
+ "muito",
94
+ "mto",
95
+ "demais",
96
+ "mano",
97
+ "mn",
98
+ "obrigado",
99
+ "obrigada",
100
+ "obg",
101
+ "vlw",
102
+ "valeu",
103
+ "ja",
104
+ "agora",
105
+ "tmj",
106
+ "kk",
107
+ "kkk",
108
+ "kkkk",
109
+ "rs",
110
+ "s",
111
+ "ss",
112
+ "sss",
113
+ ],
114
+ context_model: "haiku",
115
+ enable_on_publish: false,
116
+ quote_post: true,
117
+ };
118
+
119
+ /**
120
+ * @param {Record<string, any>} [ci] omnichat.instagram from the config
121
+ * @returns {OmnichatConfig}
122
+ */
123
+ export const resolveOmnichatConfig = (ci = {}) => ({
124
+ ...DEFAULTS,
125
+ ...ci,
126
+ interval_min: { first_48h: 1, first_week: 5, after: 30, ...(ci.interval_min ?? {}) },
127
+ });
128
+
129
+ /** A private reply is valid up to 7 days after the comment; the margin covers the clock. */
130
+ export const DM_DEADLINE = 7 * DAY - 30 * 60_000;
131
+ /** Each read goes back this much before the last seen comment (timestamps are to the second, and late). */
132
+ export const CURSOR_MARGIN = 10 * 60_000;
133
+ /** Pages of 50 comments per pass; the rest resumes on the next pass. */
134
+ export const MAX_PAGES = 20;
135
+ /** Instagram's DM text limit. */
136
+ export const DM_BYTE_LIMIT = 1000;
137
+ /** Button template limits: text (characters) and button title (characters). */
138
+ export const BUTTON_TEXT_LIMIT = 640;
139
+ export const BUTTON_TITLE_LIMIT = 20;
@@ -0,0 +1,42 @@
1
+ /* Meta errors, by what they mean for the queue. Pure.
2
+ Rate limit (4, 17, 32, 613, HTTP 429) and policy block (368) stop the whole account
3
+ for a while: insisting is what turns into a real block. Token and permission
4
+ (190, 3, 10, 200–299) also stop, without spending the comment. Server down (5xx),
5
+ network and "try again" (1, 2) retry on the next pass. The rest is not worth repeating. */
6
+
7
+ const RATE_LIMIT_CODES = new Set(["4", "17", "32", "613"]);
8
+ /** @param {string | null | undefined} c */
9
+ const isAccountCode = c =>
10
+ ["190", "3", "10"].includes(/** @type {string} */ (c)) || (+(c ?? NaN) >= 200 && +(c ?? NaN) < 300);
11
+
12
+ /**
13
+ * @param {{ code?: string | null, status?: number }} e
14
+ * @returns {"blocked" | "rate_limit" | "account" | "transient" | "permanent"}
15
+ */
16
+ export function errorType(e) {
17
+ if (e.code === "368") return "blocked";
18
+ if (RATE_LIMIT_CODES.has(/** @type {string} */ (e.code)) || e.status === 429) return "rate_limit";
19
+ if (isAccountCode(e.code)) return "account";
20
+ if (!e.status || e.status >= 500 || e.code === "1" || e.code === "2") return "transient";
21
+ return "permanent";
22
+ }
23
+
24
+ /** Thrown when the whole account must pause (rate limit, block, token). */
25
+ export class PauseError extends Error {}
26
+
27
+ /**
28
+ * The account pause that follows a PauseError (or a limit while replying by hand).
29
+ * @param {string} reason @param {number} now @param {{ rate_limit_pause_min: number }} C
30
+ * @returns {{ until: string, reason: string, at: string }}
31
+ */
32
+ export const pauseFor = (reason, now, C) => ({
33
+ until: new Date(now + C.rate_limit_pause_min * 60_000).toISOString(),
34
+ reason,
35
+ at: new Date(now).toISOString(),
36
+ });
37
+
38
+ /**
39
+ * Is the account paused?
40
+ * @param {{ until: string } | null | undefined} pause @param {number} now
41
+ */
42
+ export const isPaused = (pause, now) => !!pause && Date.parse(pause.until) > now;
@@ -0,0 +1,82 @@
1
+ /* What the content already says: the link the DM delivers and the keyword the
2
+ narration asks for. Pure text scanning; the caller gathers the texts. */
3
+
4
+ /** Social links don't count as the delivery: they are the topic's source or the post itself. */
5
+ export const SOCIAL_HOSTS = ["instagram.com", "x.com", "twitter.com", "youtube.com", "youtu.be", "tiktok.com"];
6
+ /* stops at whitespace, quotes and the markdown link close; sentence-ending punctuation is stripped after */
7
+ const URL_IN_TEXT = /https?:\/\/[^\s<>"'`()[\]{}]+/g;
8
+
9
+ /**
10
+ * The first non-social http(s) URL in a text.
11
+ * @param {string} text
12
+ * @returns {string | null}
13
+ */
14
+ export function firstLink(text) {
15
+ for (const [raw] of text.matchAll(URL_IN_TEXT)) {
16
+ const link = raw.replace(/[.,;:!?*]+$/, "");
17
+ let host;
18
+ try {
19
+ host = new URL(link).hostname.toLowerCase();
20
+ } catch {
21
+ continue;
22
+ }
23
+ if (SOCIAL_HOSTS.some(d => host === d || host.endsWith(`.${d}`))) continue;
24
+ return link;
25
+ }
26
+ return null;
27
+ }
28
+
29
+ /**
30
+ * The first link among labelled sources, in order.
31
+ * @param {Array<[string, string]>} sources [found_in, text]
32
+ * @returns {{ link: string, found_in: string } | null}
33
+ */
34
+ export function linkIn(sources) {
35
+ for (const [foundIn, text] of sources) {
36
+ const link = firstLink(text);
37
+ if (link) return { link, found_in: foundIn };
38
+ }
39
+ return null;
40
+ }
41
+
42
+ /* "Comenta REA que eu te mando o link": the word after comentar, only with the promise to send
43
+ in the same sentence ("comenta o que achou" is not a request) */
44
+ const REQUEST_PATTERN =
45
+ /\bcoment(?:a|e|em|ar)\s+(?:(?:a[ií]|aqui|embaixo|abaixo|a\s+palavra|o\s+termo)\s+)*["“'«]?([\p{L}\p{N}][\p{L}\p{N}_-]*)["”'»]?[^.!?\n]*?\b(?:mando|envio|passo|mandarei|enviarei|mandamos|enviamos)\b/giu;
46
+ const NOT_KEYWORD = new Set([
47
+ "o",
48
+ "a",
49
+ "os",
50
+ "as",
51
+ "um",
52
+ "uma",
53
+ "que",
54
+ "se",
55
+ "sua",
56
+ "seu",
57
+ "suas",
58
+ "seus",
59
+ "isso",
60
+ "esse",
61
+ "essa",
62
+ "aqui",
63
+ "ai",
64
+ "aí",
65
+ "no",
66
+ "na",
67
+ "pra",
68
+ "para",
69
+ ]);
70
+
71
+ /**
72
+ * The keyword the first matching text asks for, upper-cased, or null.
73
+ * @param {string[]} texts in priority order (the voice lines first, then the script)
74
+ * @returns {string | null}
75
+ */
76
+ export function keywordIn(texts) {
77
+ for (const t of texts) {
78
+ for (const [, keyword] of t.matchAll(REQUEST_PATTERN))
79
+ if (!NOT_KEYWORD.has(keyword.toLowerCase())) return keyword.toUpperCase();
80
+ }
81
+ return null;
82
+ }