@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,234 @@
1
+ /* ============================================================================
2
+ Fan-out: one fork per scene for a stage (composition, wireframes, narration),
3
+ and the guard that makes the same scene impossible to run twice at once.
4
+
5
+ On 2026-10-17 the composition of 2026-10-16-https-x-ai-news-grok ran twice:
6
+ the orchestrator launched the 8 scene forks and an av-motion fork launched
7
+ another 8, for the same scenes, writing the same files (~US$ 11 lost). Prompt
8
+ rules alone don't stop that; this file does, by construction:
9
+
10
+ - `plan` hands each scene out once. A scene already handed out (planned or
11
+ running, and younger than the TTL) is listed as busy and not handed out
12
+ again: a second `plan`, from whoever, has nothing to launch.
13
+ - a fork starts with `claim`: the scene must be in the fork's run and not
14
+ started yet. A second fork with the same prompt, or one from another run,
15
+ is refused (exit 3) and stops before writing anything.
16
+ - `done` closes the scene; `release` frees a scene whose fork died, so the
17
+ orchestrator can hand it out again.
18
+
19
+ State: videos/<slug>/.fanout/<kind>.json, one item per scene:
20
+ { "c03": { "run": "r-k2f9", "state": "planned"|"started"|"done", "at": ISO, "started_at"?: ISO } }
21
+
22
+ `withScenesLock` is the lock every parallel writer of scenes.json takes
23
+ (`compose.mjs apply`, `narrate.mjs apply`).
24
+ ============================================================================ */
25
+
26
+ import { readFileSync, writeFileSync, existsSync, mkdirSync, rmSync, renameSync, statSync } from "node:fs";
27
+ import { join } from "node:path";
28
+ import { randomBytes } from "node:crypto";
29
+
30
+ export const KINDS = ["compose", "wireframe", "narration"];
31
+ export const DEFAULT_TTL_MIN = 45;
32
+
33
+ export class FanoutError extends Error {
34
+ constructor(msg, code = 3) {
35
+ super(msg);
36
+ this.code = code;
37
+ }
38
+ }
39
+
40
+ const stateFile = (dir, kind) => join(dir, ".fanout", `${kind}.json`);
41
+
42
+ function checkKind(kind) {
43
+ if (!KINDS.includes(kind)) throw new FanoutError(`tipo desconhecido: ${kind} (${KINDS.join(", ")})`, 2);
44
+ }
45
+
46
+ function read(dir, kind) {
47
+ try {
48
+ return JSON.parse(readFileSync(stateFile(dir, kind), "utf8"));
49
+ } catch {
50
+ return {};
51
+ }
52
+ }
53
+
54
+ function write(dir, kind, items) {
55
+ mkdirSync(join(dir, ".fanout"), { recursive: true });
56
+ const file = stateFile(dir, kind);
57
+ const tmp = `${file}.${process.pid}.tmp`;
58
+ writeFileSync(tmp, JSON.stringify(items, null, 2) + "\n");
59
+ renameSync(tmp, file);
60
+ }
61
+
62
+ /* a lock directory: mkdir is atomic. One left by a call that died is stale after 60 s */
63
+ async function withLock(path, fn, { waitMs = 30_000, staleMs = 60_000 } = {}) {
64
+ const until = Date.now() + waitMs;
65
+ for (;;) {
66
+ try {
67
+ mkdirSync(path);
68
+ break;
69
+ } catch {
70
+ try {
71
+ if (Date.now() - statSync(path).mtimeMs > staleMs) rmSync(path, { recursive: true, force: true });
72
+ } catch {}
73
+ if (Date.now() > until) throw new FanoutError(`lock ocupado há mais de ${waitMs / 1000} s: ${path}`, 1);
74
+ await new Promise(r => setTimeout(r, 100));
75
+ }
76
+ }
77
+ try {
78
+ return await fn();
79
+ } finally {
80
+ rmSync(path, { recursive: true, force: true });
81
+ }
82
+ }
83
+
84
+ export const withScenesLock = (dir, fn) => withLock(join(dir, ".scenes.lock"), fn);
85
+ const withStateLock = (dir, fn) => withLock(join(dir, ".fanout.lock"), fn);
86
+
87
+ const ageMin = (iso, now) => (now - Date.parse(iso)) / 60_000;
88
+
89
+ /* planned or started, and younger than the TTL: someone holds the scene */
90
+ function holding(item, now, ttlMin) {
91
+ return item && item.state !== "done" && ageMin(item.started_at ?? item.at, now) < ttlMin;
92
+ }
93
+
94
+ const newRun = prefix => `${prefix}-${randomBytes(3).toString("hex")}`;
95
+
96
+ /**
97
+ * Hands each scene out once. Returns the new run (null when nothing was handed
98
+ * out), the scenes in it and the ones someone else still holds.
99
+ * @param {string} dir video folder
100
+ * @param {string} kind
101
+ * @param {string[]} ids scenes to hand out
102
+ */
103
+ export async function plan(dir, kind, ids, { ttlMin = DEFAULT_TTL_MIN, now = Date.now() } = {}) {
104
+ checkKind(kind);
105
+ return withStateLock(dir, () => {
106
+ const items = read(dir, kind);
107
+ const run = newRun("r");
108
+ const planned = [],
109
+ busy = [];
110
+ for (const id of ids) {
111
+ const it = items[id];
112
+ if (holding(it, now, ttlMin))
113
+ busy.push({ id, run: it.run, state: it.state, age_min: Math.round(ageMin(it.started_at ?? it.at, now)) });
114
+ else {
115
+ items[id] = { run, state: "planned", at: new Date(now).toISOString() };
116
+ planned.push(id);
117
+ }
118
+ }
119
+ if (planned.length) write(dir, kind, items);
120
+ return { run: planned.length ? run : null, planned, busy };
121
+ });
122
+ }
123
+
124
+ /**
125
+ * A fork takes its scene before writing anything. With `run`, the scene must be
126
+ * planned in that run and not started; without it (a scene call made alone), the
127
+ * scene must be free, and a run of its own is created.
128
+ */
129
+ export async function claim(dir, kind, id, run, { ttlMin = DEFAULT_TTL_MIN, now = Date.now() } = {}) {
130
+ checkKind(kind);
131
+ return withStateLock(dir, () => {
132
+ const items = read(dir, kind);
133
+ const it = items[id];
134
+ const at = new Date(now).toISOString();
135
+ if (!run) {
136
+ if (holding(it, now, ttlMin))
137
+ throw new FanoutError(
138
+ `${id} já está em ${it.state === "started" ? "andamento" : "fila"} na rodada ${it.run} ` +
139
+ `(há ${Math.round(ageMin(it.started_at ?? it.at, now))} min): não rode de novo`,
140
+ );
141
+ const solo = newRun("s");
142
+ items[id] = { run: solo, state: "started", at, started_at: at };
143
+ write(dir, kind, items);
144
+ return solo;
145
+ }
146
+ if (!it || it.run !== run)
147
+ throw new FanoutError(
148
+ it && holding(it, now, ttlMin)
149
+ ? `${id} pertence à rodada ${it.run}, não à ${run}: outro fork cuida dela. Pare sem escrever nada`
150
+ : `${id} não foi distribuída na rodada ${run}: pare sem escrever nada`,
151
+ );
152
+ if (it.state === "started")
153
+ throw new FanoutError(`${id} já foi iniciada por outro fork da rodada ${run}: pare sem escrever nada`);
154
+ if (it.state === "done") throw new FanoutError(`${id} já foi concluída na rodada ${run}`);
155
+ if (!holding(it, now, ttlMin)) throw new FanoutError(`a rodada ${run} de ${id} expirou: rode o plan de novo`);
156
+ items[id] = { ...it, state: "started", started_at: at };
157
+ write(dir, kind, items);
158
+ return run;
159
+ });
160
+ }
161
+
162
+ /** The fork finished its scene (with or without success: the stage's gate says the rest). */
163
+ export async function done(dir, kind, id, run, { now = Date.now() } = {}) {
164
+ checkKind(kind);
165
+ return withStateLock(dir, () => {
166
+ const items = read(dir, kind);
167
+ const it = items[id];
168
+ if (!it || it.run !== run) throw new FanoutError(`${id} não está na rodada ${run}`, 1);
169
+ items[id] = { ...it, state: "done", done_at: new Date(now).toISOString() };
170
+ write(dir, kind, items);
171
+ });
172
+ }
173
+
174
+ /** Frees scenes whose fork died (error, no report), so `plan` hands them out again. */
175
+ export async function release(dir, kind, ids) {
176
+ checkKind(kind);
177
+ return withStateLock(dir, () => {
178
+ const items = read(dir, kind);
179
+ const freed = ids.filter(id => items[id] && items[id].state !== "done");
180
+ for (const id of freed) delete items[id];
181
+ write(dir, kind, items);
182
+ return freed;
183
+ });
184
+ }
185
+
186
+ /** Who holds what right now: scenes planned or running, younger than the TTL. */
187
+ export function busy(dir, kind, { ttlMin = DEFAULT_TTL_MIN, now = Date.now() } = {}) {
188
+ checkKind(kind);
189
+ const items = read(dir, kind);
190
+ return Object.entries(items)
191
+ .filter(([, it]) => holding(it, now, ttlMin))
192
+ .map(([id, it]) => ({
193
+ id,
194
+ run: it.run,
195
+ state: it.state,
196
+ age_min: Math.round(ageMin(it.started_at ?? it.at, now)),
197
+ }));
198
+ }
199
+
200
+ export const hasState = (dir, kind) => existsSync(stateFile(dir, kind));
201
+
202
+ /** The Agent tool takes an alias; the config may hold a full id */
203
+ export function agentModel(model) {
204
+ return ["fable", "opus", "sonnet", "haiku"].find(a => String(model).includes(a)) ?? "opus";
205
+ }
206
+
207
+ /**
208
+ * Splits ids into waves of at most `parallel`.
209
+ * @param {string[]} ids
210
+ */
211
+ export function waves(ids, parallel) {
212
+ const out = [];
213
+ for (let i = 0; i < ids.length; i += parallel) out.push(ids.slice(i, i + parallel));
214
+ return out;
215
+ }
216
+
217
+ /** The lines a `plan` prints for the orchestrator: waves, Agent parameters and each prompt. */
218
+ export function printPlan({ title, run, waves: ws, model, effort, prompt, busy: held, after }) {
219
+ console.log(title);
220
+ if (held.length)
221
+ console.log(
222
+ `em andamento (não lance de novo): ${held.map(b => `${b.id} (rodada ${b.run}, ${b.state === "started" ? "rodando" : "na fila"} há ${b.age_min} min)`).join(", ")}`,
223
+ );
224
+ if (!run) return;
225
+ console.log(`rodada: ${run}`);
226
+ ws.forEach((w, i) => console.log(`onda ${i + 1}: ${w.join(" ")}`));
227
+ console.log(
228
+ `\nCada onda é UMA mensagem com uma chamada Agent por cena (subagent_type general-purpose, ` +
229
+ `model ${model}, effort ${effort}, em primeiro plano); a próxima onda só depois de todos os relatórios. ` +
230
+ `Este plan não se repete: rodar de novo não distribui o que já está nesta rodada. Prompts:`,
231
+ );
232
+ for (const w of ws) for (const id of w) console.log(` ${id}: ${prompt(id)}`);
233
+ if (after) console.log(`\n${after}`);
234
+ }
@@ -0,0 +1,94 @@
1
+ /* ============================================================================
2
+ HTTP shared by every platform client: one request with the platforms'
3
+ readable error. Pure: `fetch` is injected (defaults to the global one), so
4
+ the same code runs on the CLI, in a cloud function and in tests.
5
+ ============================================================================ */
6
+
7
+ /**
8
+ * @typedef {typeof globalThis.fetch} Fetch
9
+ * @typedef {Error & { code?: string | null, status?: number }} PlatformError
10
+ * @typedef {{ r: Response, j: any, text: string }} HttpResult
11
+ */
12
+
13
+ /**
14
+ * One HTTP call. On a non-2xx answer throws `label: <status> <message>` with
15
+ * `code` (the platform's error code, when there is one) and `status`.
16
+ * @param {string} url
17
+ * @param {RequestInit} [opts]
18
+ * @param {string} [label]
19
+ * @param {{ fetch?: Fetch }} [deps]
20
+ * @returns {Promise<HttpResult>}
21
+ */
22
+ export async function request(url, opts, label, { fetch: f = globalThis.fetch } = {}) {
23
+ const r = await f(url, opts);
24
+ const text = await r.text();
25
+ let j = null;
26
+ try {
27
+ j = JSON.parse(text);
28
+ } catch {
29
+ /* not JSON */
30
+ }
31
+ if (!r.ok) {
32
+ const e = j?.error;
33
+ const msg =
34
+ e?.error_user_msg ??
35
+ e?.message ??
36
+ j?.error_description ??
37
+ (typeof e === "string" ? e : null) ??
38
+ text.slice(0, 400);
39
+ /* the code says more than the message (TikTok's is sometimes just "read the guidelines") */
40
+ const code = typeof e === "object" && e?.code != null && e.code !== "ok" ? String(e.code) : null;
41
+ /** @type {PlatformError} */
42
+ const error = new Error(`${label}: ${r.status} ${msg}${code && !String(msg).includes(code) ? ` (${code})` : ""}`);
43
+ error.code = code;
44
+ error.status = r.status;
45
+ throw error;
46
+ }
47
+ return { r, j, text };
48
+ }
49
+
50
+ /**
51
+ * @param {number} ms
52
+ * @returns {Promise<void>}
53
+ */
54
+ export const sleep = ms => new Promise(ok => setTimeout(ok, ms));
55
+
56
+ /** One day in ms. */
57
+ export const DAY = 86_400_000;
58
+ /** One hour in ms. */
59
+ export const HOUR = 3_600_000;
60
+
61
+ /**
62
+ * Deps every core function accepts. All optional: the defaults are the real ones.
63
+ * @typedef {object} Deps
64
+ * @property {Fetch} [fetch]
65
+ * @property {() => number} [now] epoch ms
66
+ * @property {(ms: number) => Promise<void>} [sleep]
67
+ * @property {(line: string) => void} [log] stdout-like progress
68
+ * @property {(line: string) => void} [warn] stderr-like warnings
69
+ * @property {() => number} [random] in [0, 1)
70
+ */
71
+
72
+ /**
73
+ * Fills the missing deps with the real ones.
74
+ * @param {Deps} [deps]
75
+ * @returns {Required<Deps>}
76
+ */
77
+ export function withDefaults(deps = {}) {
78
+ return {
79
+ fetch: deps.fetch ?? globalThis.fetch,
80
+ now: deps.now ?? Date.now,
81
+ sleep: deps.sleep ?? sleep,
82
+ log: deps.log ?? (() => {}),
83
+ warn: deps.warn ?? (() => {}),
84
+ random: deps.random ?? Math.random,
85
+ };
86
+ }
87
+
88
+ /**
89
+ * Token storage: where the platform tokens live (the .env on the CLI, a secret
90
+ * store in the cloud). `get` returns "" when missing.
91
+ * @typedef {object} TokenStore
92
+ * @property {(name: string) => string} get
93
+ * @property {(values: Record<string, string>) => void | Promise<void>} set
94
+ */
@@ -0,0 +1,14 @@
1
+ /* Entry for `node --test tools/lib`: node resolves the folder to this file, which loads every
2
+ *.test.mjs below it into the same test run. Not a library entry point: import the modules directly. */
3
+ const { readdirSync } = require("node:fs");
4
+ const { join } = require("node:path");
5
+ const { pathToFileURL } = require("node:url");
6
+
7
+ const walk = dir =>
8
+ readdirSync(dir, { withFileTypes: true }).flatMap(e =>
9
+ e.isDirectory() ? walk(join(dir, e.name)) : e.name.endsWith(".test.mjs") ? [join(dir, e.name)] : [],
10
+ );
11
+
12
+ (async () => {
13
+ for (const file of walk(__dirname).sort()) await import(pathToFileURL(file).href);
14
+ })();
@@ -0,0 +1,69 @@
1
+ /* A minimal MCP server over stdio (JSON-RPC 2.0, one message per line), enough for the plugin's tools:
2
+ initialize, tools/list, tools/call and ping. The `omni mcp` command serves its tools with it
3
+ (docs/specs/phase-2-plugin-cli.md, step 4); no SDK, so the CLI carries no extra dependency.
4
+
5
+ A tool is { name, description, inputSchema, run(args) → string }; its text goes back as the result, and
6
+ a thrown Error goes back as a tool error the model can read. */
7
+
8
+ import { createInterface } from "node:readline";
9
+
10
+ const PROTOCOL = "2025-06-18";
11
+
12
+ /**
13
+ * @typedef {{ name: string, description: string, inputSchema: object, run: (args: any) => Promise<string> | string }} Tool
14
+ * @param {{ name: string, version: string, tools: Tool[], input?: NodeJS.ReadableStream, output?: NodeJS.WritableStream }} o
15
+ * @returns {Promise<void>} resolves when the input ends
16
+ */
17
+ export function serveMcp({ name, version, tools, input = process.stdin, output = process.stdout }) {
18
+ const send = (/** @type {unknown} */ msg) => output.write(`${JSON.stringify(msg)}\n`);
19
+ const reply = (/** @type {unknown} */ id, /** @type {unknown} */ result) => send({ jsonrpc: "2.0", id, result });
20
+ const error = (/** @type {unknown} */ id, /** @type {number} */ code, /** @type {string} */ message) =>
21
+ send({ jsonrpc: "2.0", id, error: { code, message } });
22
+
23
+ /** @param {any} msg */
24
+ async function handle(msg) {
25
+ const { id, method, params } = msg ?? {};
26
+ const isRequest = id !== undefined && id !== null;
27
+ if (method === "initialize")
28
+ return reply(id, {
29
+ protocolVersion: params?.protocolVersion ?? PROTOCOL,
30
+ capabilities: { tools: {} },
31
+ serverInfo: { name, version },
32
+ });
33
+ if (method === "ping") return reply(id, {});
34
+ if (method === "tools/list")
35
+ return reply(id, {
36
+ tools: tools.map(({ name: n, description, inputSchema }) => ({ name: n, description, inputSchema })),
37
+ });
38
+ if (method === "tools/call") {
39
+ const tool = tools.find(t => t.name === params?.name);
40
+ if (!tool) return error(id, -32602, `ferramenta desconhecida: ${params?.name}`);
41
+ try {
42
+ const text = await tool.run(params?.arguments ?? {});
43
+ return reply(id, { content: [{ type: "text", text: String(text) }] });
44
+ } catch (e) {
45
+ return reply(id, {
46
+ content: [{ type: "text", text: String(/** @type {Error} */ (e).message) }],
47
+ isError: true,
48
+ });
49
+ }
50
+ }
51
+ /* notifications (initialized, cancelled…) need no answer */
52
+ if (isRequest) error(id, -32601, `método não suportado: ${method}`);
53
+ }
54
+
55
+ return new Promise(resolve => {
56
+ const rl = createInterface({ input });
57
+ rl.on("line", line => {
58
+ if (!line.trim()) return;
59
+ let msg;
60
+ try {
61
+ msg = JSON.parse(line);
62
+ } catch {
63
+ return error(null, -32700, "JSON inválido");
64
+ }
65
+ void handle(msg);
66
+ });
67
+ rl.on("close", () => resolve());
68
+ });
69
+ }
@@ -0,0 +1,204 @@
1
+ /* ============================================================================
2
+ Reel metrics: what is due at a given age (the age milestones and the
3
+ periodic reading), the snapshot shape, and the collection of one video and
4
+ of the account. Pure: the record comes in and goes out; the caller reads
5
+ and writes metrics.json / account.json. Read only on Instagram.
6
+ ============================================================================ */
7
+
8
+ import { HOUR, withDefaults } from "../http.mjs";
9
+
10
+ /** @typedef {import("../http.mjs").Deps} Deps */
11
+ /** @typedef {import("../platforms/instagram.mjs").InstagramClient} InstagramClient */
12
+
13
+ /** Every how many hours the collector runs. */
14
+ export const AGENT_STEP_H = 4;
15
+
16
+ /** The age milestones: [name, hours]. */
17
+ export const MILESTONES = /** @type {const} */ ([
18
+ ["1h", 1],
19
+ ["3h", 3],
20
+ ["24h", 24],
21
+ ["72h", 72],
22
+ ["7d", 168],
23
+ ["30d", 720],
24
+ ]);
25
+
26
+ /**
27
+ * How often the Reel is read, by its age (hours): every step in the first 30 days, then daily.
28
+ * @param {number} ageH
29
+ */
30
+ export const currentInterval = ageH => (ageH < 720 ? AGENT_STEP_H : 24);
31
+
32
+ export const BASE = ["views", "reach", "likes", "comments", "saved", "shares", "total_interactions"];
33
+ export const REEL = ["ig_reels_avg_watch_time", "ig_reels_video_view_total_time", "reels_skip_rate"];
34
+
35
+ /**
36
+ * @typedef {object} Snapshot
37
+ * @property {string} at
38
+ * @property {number} age_h
39
+ * @property {number | null} views
40
+ * @property {number | null} reach
41
+ * @property {number | null} likes
42
+ * @property {number | null} comments
43
+ * @property {number | null} saves
44
+ * @property {number | null} shares
45
+ * @property {number | null} interactions
46
+ * @property {number | null} avg_watch_s
47
+ * @property {number | null} watched_pct
48
+ * @property {number | null} skip_3s_pct
49
+ * @property {number | null} total_watch_h
50
+ */
51
+
52
+ /**
53
+ * @typedef {object} VideoRecord metrics.json `instagram`
54
+ * @property {string} id
55
+ * @property {string} published_at
56
+ * @property {number | null} [duration_s]
57
+ * @property {Array<Snapshot & { milestone: string }>} snapshots
58
+ * @property {Snapshot[]} [hourly]
59
+ * @property {Snapshot} [current]
60
+ * @property {{ at: string, message: string } | null} [error]
61
+ * @property {boolean} [removed]
62
+ */
63
+
64
+ /**
65
+ * One reading from the raw insights.
66
+ * @param {Record<string, any>} m raw metrics by name
67
+ * @param {number | null | undefined} duration seconds of the uploaded MP4
68
+ * @param {number} ageH
69
+ * @param {string} at ISO
70
+ * @returns {Snapshot}
71
+ */
72
+ export function snapshot(m, duration, ageH, at) {
73
+ const avg = m.ig_reels_avg_watch_time != null ? Math.round(m.ig_reels_avg_watch_time / 100) / 10 : null;
74
+ return {
75
+ at,
76
+ age_h: Math.round(ageH * 10) / 10,
77
+ views: m.views ?? null,
78
+ reach: m.reach ?? null,
79
+ likes: m.likes ?? null,
80
+ comments: m.comments ?? null,
81
+ saves: m.saved ?? null,
82
+ shares: m.shares ?? null,
83
+ interactions: m.total_interactions ?? null,
84
+ avg_watch_s: avg,
85
+ watched_pct: avg != null && duration ? Math.round((avg / duration) * 1000) / 10 : null,
86
+ skip_3s_pct: m.reels_skip_rate ?? null,
87
+ total_watch_h:
88
+ m.ig_reels_video_view_total_time != null ? Math.round((m.ig_reels_video_view_total_time / HOUR) * 10) / 10 : null,
89
+ };
90
+ }
91
+
92
+ /**
93
+ * What is due now: the milestones in their window and the current reading if due.
94
+ * A milestone is only taken near its age (up to half a milestone later, at least 1 h):
95
+ * a late snapshot would be skewed, so a missed window stays missed.
96
+ * @param {{ snapshots?: Array<{ milestone: string }>, current?: { at: string } }} rec
97
+ * @param {number} ageH
98
+ * @param {boolean} force
99
+ * @param {number} now epoch ms
100
+ * @returns {{ milestones: string[], current: boolean }}
101
+ */
102
+ export function due(rec, ageH, force, now) {
103
+ const taken = new Set((rec.snapshots ?? []).map(f => f.milestone));
104
+ const milestones = MILESTONES.filter(
105
+ ([name, h]) => !taken.has(name) && ageH >= h && ageH <= h + Math.max(1, h / 2),
106
+ ).map(([name]) => name);
107
+ const step = currentInterval(ageH);
108
+ const last = rec.current ? Date.parse(rec.current.at) : 0;
109
+ const current = force || now - last >= step * HOUR * 0.95;
110
+ return { milestones, current };
111
+ }
112
+
113
+ /**
114
+ * A media's insights; a post that is not a Reel lacks the Reel metrics (the whole call would fail).
115
+ * @param {InstagramClient} client @param {string} id
116
+ */
117
+ export async function reelInsights(client, id) {
118
+ try {
119
+ return await client.insights(id, [...BASE, ...REEL]);
120
+ } catch (e) {
121
+ const err = /** @type {import("../http.mjs").PlatformError} */ (e);
122
+ if (err.status === 400 && /metric/i.test(err.message)) return client.insights(id, BASE);
123
+ throw e;
124
+ }
125
+ }
126
+
127
+ /**
128
+ * Collects one video if something is due.
129
+ * @param {{ client: InstagramClient, ig: { id: string, published_at: string }, stored?: VideoRecord | null,
130
+ * duration: () => number | null, force: boolean, retryRemoved?: boolean } & Deps} p
131
+ * `stored` is metrics.json's `instagram`; `duration` is only called when the record has none
132
+ * @returns {Promise<null | { rec: VideoRecord, milestones: string[], error: string | null }>}
133
+ * null when nothing was due (nothing to write)
134
+ */
135
+ export async function collectVideo({ client, ig, stored, duration, force, retryRemoved = force, ...deps }) {
136
+ const d = withDefaults(deps);
137
+ /* Reel republished: another id, another count */
138
+ /** @type {VideoRecord} */
139
+ const rec = stored?.id === ig.id ? stored : { id: ig.id, published_at: ig.published_at, snapshots: [] };
140
+ if (rec.removed && !retryRemoved) return null;
141
+ const ageH = (d.now() - Date.parse(ig.published_at)) / HOUR;
142
+ const { milestones, current } = due(rec, ageH, force, d.now());
143
+ if (!milestones.length && !current) return null;
144
+ rec.duration_s ??= duration();
145
+ try {
146
+ const f = snapshot(await reelInsights(client, ig.id), rec.duration_s, ageH, new Date(d.now()).toISOString());
147
+ for (const milestone of milestones) rec.snapshots.push({ milestone, ...f });
148
+ rec.hourly = [...(rec.hourly ?? []), f];
149
+ rec.current = f;
150
+ rec.error = null;
151
+ } catch (e) {
152
+ const err = /** @type {import("../http.mjs").PlatformError} */ (e);
153
+ rec.error = { at: new Date(d.now()).toISOString(), message: err.message };
154
+ /* the Reel was deleted: stop asking (collect with the video named tries again) */
155
+ if (err.status === 400 && /does not exist|cannot be loaded|nonexisting/i.test(err.message)) rec.removed = true;
156
+ }
157
+ return { rec, milestones, error: rec.error?.message ?? null };
158
+ }
159
+
160
+ /**
161
+ * @typedef {object} AccountRecord .metrics/account.json
162
+ * @property {Array<{ day: string, at: string, username: string, followers: number | null, following: number | null,
163
+ * posts: number | null, profile_visits_day: number | null }>} snapshots
164
+ * @property {Array<{ at: string, followers: number | null, profile_visits_day: number | null }>} [hourly]
165
+ */
166
+
167
+ /**
168
+ * The account: one snapshot per day, redone every pass (today's is the newest),
169
+ * and every reading in `hourly`.
170
+ * @param {{ client: InstagramClient, stored?: AccountRecord | null, force: boolean, day?: (now: number) => string } & Deps} p
171
+ * `day` is the calendar day of a time (the runtime's local day by default)
172
+ * @returns {Promise<AccountRecord | null>} null when nothing was due
173
+ */
174
+ export async function collectAccount({ client, stored, force, day, ...deps }) {
175
+ const d = withDefaults(deps);
176
+ const account = stored ?? { snapshots: [] };
177
+ const today = (day ?? (t => new Date(t).toLocaleDateString("sv-SE")))(d.now());
178
+ const last = account.snapshots.at(-1);
179
+ if (!force && last?.day === today && d.now() - Date.parse(last.at) < HOUR * 0.95) return null;
180
+ const me = await client.account("username,followers_count,follows_count,media_count");
181
+ let visits = null;
182
+ try {
183
+ visits = await client.profileVisits();
184
+ } catch {
185
+ /* small account or no permission: goes without */
186
+ }
187
+ const f = {
188
+ day: today,
189
+ at: new Date(d.now()).toISOString(),
190
+ username: me.username,
191
+ followers: me.followers_count ?? null,
192
+ following: me.follows_count ?? null,
193
+ posts: me.media_count ?? null,
194
+ profile_visits_day: visits,
195
+ };
196
+ if (account.snapshots.at(-1)?.day === today) account.snapshots[account.snapshots.length - 1] = f;
197
+ else account.snapshots.push(f);
198
+ /* every reading also goes into hourly: the followers curve comes from it */
199
+ account.hourly = [
200
+ ...(account.hourly ?? []),
201
+ { at: f.at, followers: f.followers, profile_visits_day: f.profile_visits_day },
202
+ ];
203
+ return account;
204
+ }
@@ -0,0 +1,15 @@
1
+ /* "Sem narração e voz": content.json `narration: false`. The video has no spoken script, narration, voice or
2
+ captions; scenes.json holds beats with `t` and a `min_duration` per scene, and the assembly is picture,
3
+ effects and the bed. Absent means narrated. `omnivideo.py narrated()` reads the same flag. */
4
+
5
+ import { readFileSync } from "node:fs";
6
+ import { join } from "node:path";
7
+
8
+ /** @param {string} videoDir */
9
+ export function isNarrated(videoDir) {
10
+ try {
11
+ return JSON.parse(readFileSync(join(videoDir, "content.json"), "utf8")).narration !== false;
12
+ } catch {
13
+ return true;
14
+ }
15
+ }