@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,126 @@
1
+ /* `omni skill <name>`: what a plugin header's `!` line runs (docs/specs/phase-2-plugin-cli.md, step 3).
2
+ It asks the registry for one part of the skill's body with this machine's login, writes the skill's
3
+ sibling files to a temporary folder, fills the release's placeholders ({{OMNI_CODE}} with the code
4
+ root, {{OMNI_SKILLS}}/<name>/ with that folder) and returns the text the model reads.
5
+
6
+ Whatever happens, it returns text and never fails: Claude Code puts the output where the skill's
7
+ instructions go, so a refusal has to be readable instructions too ("pare e avise o usuário"). No
8
+ network, no skill: there is no offline copy.
9
+
10
+ The folder is $TMPDIR/omni-skills/<name>@<hash>/ (0700, files 0600). It goes away on a refusal for
11
+ that skill and after an hour unused, swept on every call. */
12
+
13
+ import { chmodSync, mkdirSync, readdirSync, rmSync, statSync, utimesSync, writeFileSync } from "node:fs";
14
+ import { tmpdir } from "node:os";
15
+ import { dirname, join, normalize, relative } from "node:path";
16
+
17
+ export const UNUSED_MS = 60 * 60_000;
18
+
19
+ const stop = (/** @type {string} */ why) =>
20
+ `Pare aqui: não siga esta skill nem a refaça de memória. Avise o usuário, em português: ${why}`;
21
+
22
+ /** @param {string} base @param {() => number} now */
23
+ function sweep(base, now) {
24
+ let names = [];
25
+ try {
26
+ names = readdirSync(base);
27
+ } catch {
28
+ return;
29
+ }
30
+ for (const n of names) {
31
+ const dir = join(base, n);
32
+ try {
33
+ if (now() - statSync(dir).mtimeMs > UNUSED_MS) rmSync(dir, { recursive: true, force: true });
34
+ } catch {
35
+ /* gone already */
36
+ }
37
+ }
38
+ }
39
+
40
+ /** removes every folder of one skill (a refusal) @param {string} base @param {string} name */
41
+ function wipe(base, name) {
42
+ try {
43
+ for (const n of readdirSync(base))
44
+ if (n.startsWith(`${name}@`)) rmSync(join(base, n), { recursive: true, force: true });
45
+ } catch {
46
+ /* nothing there */
47
+ }
48
+ }
49
+
50
+ /** writes the files into the skill's folder, refusing any path that leaves it
51
+ * @param {string} dir @param {Record<string, string>} files */
52
+ function materialize(dir, files) {
53
+ mkdirSync(dir, { recursive: true, mode: 0o700 });
54
+ chmodSync(dir, 0o700);
55
+ for (const [path, text] of Object.entries(files)) {
56
+ const target = normalize(join(dir, path));
57
+ if (relative(dir, target).startsWith("..")) continue;
58
+ mkdirSync(dirname(target), { recursive: true, mode: 0o700 });
59
+ writeFileSync(target, text, { mode: 0o600 });
60
+ }
61
+ }
62
+
63
+ /**
64
+ * @param {{
65
+ * studioUrl: string,
66
+ * name: string,
67
+ * version?: string,
68
+ * part?: number,
69
+ * codeRoot: string,
70
+ * token: () => Promise<string | null>,
71
+ * fetch?: typeof fetch,
72
+ * base?: string,
73
+ * now?: () => number,
74
+ * }} o
75
+ * @returns {Promise<string>} the text for the model
76
+ */
77
+ export async function skillText(o) {
78
+ const base = o.base ?? join(tmpdir(), "omni-skills");
79
+ const now = o.now ?? Date.now;
80
+ const part = o.part ?? 1;
81
+ sweep(base, now);
82
+ if (!/^[a-z0-9-]{1,64}$/.test(o.name)) return stop(`o nome de skill "${o.name}" não é válido.`);
83
+
84
+ let token;
85
+ try {
86
+ token = await o.token();
87
+ } catch {
88
+ return stop(
89
+ "o OmniVideo está sem conexão, e as skills não abrem sem rede. Tente de novo quando a internet voltar.",
90
+ );
91
+ }
92
+ if (!token) return stop("este computador não está logado no OmniVideo. Rode `omni login` e peça de novo.");
93
+
94
+ const url = new URL(`/skills/${o.name}`, o.studioUrl);
95
+ if (o.version) url.searchParams.set("v", o.version);
96
+ url.searchParams.set("part", String(part));
97
+ /** @type {Response} */
98
+ let r;
99
+ try {
100
+ r = await (o.fetch ?? fetch)(url, { headers: { authorization: `Bearer ${token}` } });
101
+ } catch {
102
+ return stop(
103
+ "o OmniVideo está sem conexão, e as skills não abrem sem rede. Tente de novo quando a internet voltar.",
104
+ );
105
+ }
106
+ const j = /** @type {any} */ (await r.json().catch(() => ({})));
107
+ if (r.status === 401)
108
+ return stop("o login deste computador no OmniVideo não vale mais. Rode `omni login` e peça de novo.");
109
+ if (r.status === 403) {
110
+ wipe(base, o.name);
111
+ return stop(
112
+ `${j.error ?? "a assinatura do OmniVideo não está ativa."} Os vídeos continuam no computador; as skills voltam com o plano.`,
113
+ );
114
+ }
115
+ if (!r.ok || typeof j.body !== "string")
116
+ return stop(`o OmniVideo não entregou a skill ${o.name} (${j.error ?? `HTTP ${r.status}`}).`);
117
+
118
+ const dir = join(base, `${o.name}@${String(j.hash).slice(0, 16)}`);
119
+ if (part === 1 && j.files && Object.keys(j.files).length) materialize(dir, j.files);
120
+ try {
121
+ utimesSync(dir, new Date(now()), new Date(now()));
122
+ } catch {
123
+ /* no files for this skill */
124
+ }
125
+ return j.body.replaceAll("{{OMNI_CODE}}", o.codeRoot).replaceAll(`{{OMNI_SKILLS}}/${o.name}/`, `${dir}/`);
126
+ }
@@ -0,0 +1,196 @@
1
+ /* A release of the skills for the registry and the public plugin (docs/specs/phase-2-plugin-cli.md, step 3).
2
+ One source, .claude/skills, serves both this repo (as it is) and the product (as built here):
3
+
4
+ - The body is rewritten for a machine that has the CLI and not this repo: `node tools/x.mjs …` and
5
+ `$PY tools/x.py …` become `<run> x …` (by default `npx -y @crievo/omni run x …`), `templates/…` and
6
+ `reference/…` become `{{OMNI_CODE}}/templates/…` and `{{OMNI_CODE}}/reference/…` and `.claude/skills/<name>/<file>` becomes
7
+ `{{OMNI_SKILLS}}/<name>/<file>`. `omni skill` fills the two placeholders when it prints the body.
8
+ - A body longer than one `!` line carries (MAX_PART characters, measured in the encrypted-skills study)
9
+ is cut at its `## ` headings into parts; the plugin's header gets one `!` line per part.
10
+ - The header keeps the frontmatter Claude Code reads (name, description, model, effort, context) and
11
+ only the lines that fetch the body, plus the plan-B line for when shell execution in skills is off.
12
+ - Every skill gets a hash of what it serves; the release's version is the hash of all of them.
13
+
14
+ Pure: the files come in as strings, so the tests need no disk. */
15
+
16
+ import { createHash } from "node:crypto";
17
+ import { CLI_COMMAND } from "./cli.mjs";
18
+
19
+ export const MAX_PART = 24_000;
20
+ export const DEFAULT_RUN = `${CLI_COMMAND} run`;
21
+ export const DEFAULT_SKILL = `${CLI_COMMAND} skill`;
22
+ /* the frontmatter keys the header keeps; anything else stays in the private body */
23
+ const HEADER_KEYS = ["name", "description", "model", "effort", "context"];
24
+
25
+ /** @param {string} text @returns {{ meta: Record<string, string>, metaLines: string[], body: string }} */
26
+ export function splitFrontmatter(text) {
27
+ const m = text.match(/^---\n([\s\S]*?)\n---\n?/);
28
+ if (!m) return { meta: {}, metaLines: [], body: text };
29
+ const metaLines = m[1].split("\n");
30
+ /** @type {Record<string, string>} */
31
+ const meta = {};
32
+ for (const line of metaLines) {
33
+ const kv = line.match(/^([A-Za-z_-]+):\s*(.*)$/);
34
+ if (kv) meta[kv[1]] = kv[2];
35
+ }
36
+ return { meta, metaLines, body: text.slice(m[0].length) };
37
+ }
38
+
39
+ /** The body as a CLI machine runs it, and what the rewrite couldn't place.
40
+ * @param {string} body @param {{ run?: string }} [o] @returns {{ body: string, warnings: string[] }} */
41
+ export function rewriteBody(body, { run = DEFAULT_RUN } = {}) {
42
+ let out = body
43
+ .replace(/\bnode (?:\.\/)?tools\/([A-Za-z0-9_-]+)\.mjs\b/g, (_, t) => `${run} ${t}`)
44
+ .replace(/(?:"\$PY"|\$PY|\.venv\/bin\/python3?) (?:\.\/)?tools\/([A-Za-z0-9_-]+)\.py\b/g, (_, t) => `${run} ${t}`)
45
+ .replace(/(^|[^\w./{}-])(templates|reference)\//g, (_, pre, dir) => `${pre}{{OMNI_CODE}}/${dir}/`)
46
+ .replace(/(^|[^\w./{}-])\.claude\/skills\//g, (_, pre) => `${pre}{{OMNI_SKILLS}}/`);
47
+ /** @type {string[]} */
48
+ const warnings = [];
49
+ /* a command that still runs a repo tool by path would fail on a CLI machine */
50
+ for (const m of out.matchAll(/\b(?:node|python3?|bun) (?:\.\/)?tools\/[\w./-]+/g))
51
+ warnings.push(`chamada de ferramenta sem reescrita: ${m[0]}`);
52
+ out = out.replace(/\n{3,}/g, "\n\n");
53
+ return { body: out, warnings };
54
+ }
55
+
56
+ /** Cuts a body into parts of at most `max` characters, at `## ` headings, then `### `, then blank lines.
57
+ * @param {string} body @param {number} [max] @returns {string[]} */
58
+ export function splitParts(body, max = MAX_PART) {
59
+ if (body.length <= max) return [body];
60
+ /** @param {string} text @param {RegExp[]} seps @returns {string[]} */
61
+ const cut = (text, seps) => {
62
+ if (text.length <= max) return [text];
63
+ const [sep, ...rest] = seps;
64
+ if (!sep) {
65
+ /* no boundary left: a hard cut at a line end */
66
+ const at = text.lastIndexOf("\n", max) > 0 ? text.lastIndexOf("\n", max) : max;
67
+ return [text.slice(0, at), ...cut(text.slice(at), [])];
68
+ }
69
+ const pieces = text.split(sep).filter(p => p.length > 0);
70
+ /** @type {string[]} */
71
+ const parts = [];
72
+ let current = "";
73
+ for (const piece of pieces.flatMap(p => (p.length > max ? cut(p, rest) : [p]))) {
74
+ if (current && current.length + piece.length > max) {
75
+ parts.push(current);
76
+ current = "";
77
+ }
78
+ current += piece;
79
+ }
80
+ if (current) parts.push(current);
81
+ return parts;
82
+ };
83
+ return cut(body, [/(?=\n## )/, /(?=\n### )/, /(?=\n\n)/]);
84
+ }
85
+
86
+ const sha = (/** @type {string} */ s) => createHash("sha256").update(s).digest("hex");
87
+
88
+ /** The plugin's SKILL.md for one skill: the public frontmatter and the lines that fetch the body.
89
+ * @param {{ name: string, metaLines: string[], parts: number, version: string }} s @param {{ skill?: string }} [o] */
90
+ export function header(s, { skill = DEFAULT_SKILL } = {}) {
91
+ const meta = s.metaLines.filter(l => HEADER_KEYS.includes(l.split(":")[0].trim()));
92
+ const fetchLines = Array.from({ length: s.parts }, (_, i) =>
93
+ s.parts === 1
94
+ ? `!\`${skill} ${s.name} --v ${s.version}\``
95
+ : `!\`${skill} ${s.name} --v ${s.version} --part ${i + 1}/${s.parts}\``,
96
+ );
97
+ const planB =
98
+ s.parts === 1
99
+ ? `Se acima aparecer o comando em vez das instruções, rode via Bash \`${skill} ${s.name} --v ${s.version}\` e siga a saída.`
100
+ : `Se acima aparecerem os comandos em vez das instruções, rode via Bash \`${skill} ${s.name} --v ${s.version} --part i/${s.parts}\` para cada parte, de 1 a ${s.parts}, e siga a saída.`;
101
+ return `---\n${meta.join("\n")}\n---\n\n${fetchLines.join("\n\n")}\n\n${planB}\n`;
102
+ }
103
+
104
+ /**
105
+ * The release: what the registry stores and what the plugin repo gets.
106
+ * @param {{ name: string, files: Record<string, string> }[]} skills each skill's files by path in its folder
107
+ * (SKILL.md among them)
108
+ * @param {{ run?: string, skill?: string, max?: number }} [o]
109
+ */
110
+ export function buildRelease(skills, o = {}) {
111
+ /** @type {string[]} */
112
+ const warnings = [];
113
+ const built = skills
114
+ .slice()
115
+ .sort((a, b) => a.name.localeCompare(b.name))
116
+ .map(({ name, files }) => {
117
+ const source = files["SKILL.md"];
118
+ if (source === undefined) throw new Error(`${name}: falta SKILL.md`);
119
+ const { meta, metaLines, body } = splitFrontmatter(source);
120
+ if (meta.name !== name) throw new Error(`${name}: o name do frontmatter é ${meta.name ?? "nenhum"}`);
121
+ const rewritten = rewriteBody(body, o);
122
+ warnings.push(...rewritten.warnings.map(w => `${name}: ${w}`));
123
+ const parts = splitParts(rewritten.body, o.max);
124
+ /** @type {Record<string, string>} */
125
+ const extra = {};
126
+ for (const [path, text] of Object.entries(files))
127
+ if (path !== "SKILL.md") extra[path] = rewriteBody(text, o).body;
128
+ const hash = sha(JSON.stringify({ parts, extra }));
129
+ return { name, meta, metaLines, parts, files: extra, hash };
130
+ });
131
+ const version = sha(built.map(s => `${s.name}:${s.hash}`).join("\n")).slice(0, 12);
132
+ return {
133
+ version,
134
+ warnings,
135
+ skills: built.map(s => ({
136
+ name: s.name,
137
+ hash: s.hash,
138
+ parts: s.parts,
139
+ files: s.files,
140
+ size: s.parts.join("").length,
141
+ })),
142
+ headers: Object.fromEntries(
143
+ built.map(s => [s.name, header({ name: s.name, metaLines: s.metaLines, parts: s.parts.length, version }, o)]),
144
+ ),
145
+ };
146
+ }
147
+
148
+ /**
149
+ * The public plugin, ready to be the marketplace repo (pedroflp/omni-plugin): the manifest, the
150
+ * marketplace that lists it, the SessionStart hook and the MCP server, both through the CLI, and the
151
+ * skills' headers. Its version is the hash of everything else, so Claude Code updates it only when some
152
+ * file changed.
153
+ * @param {{ headers: Record<string, string> }} release
154
+ * @param {{ cli: string, owner?: string }} o cli: the command that runs the CLI (`npx -y @crievo/omni`)
155
+ * @returns {Record<string, string>} files by path
156
+ */
157
+ export function pluginFiles(release, { cli, owner = "pedroflp" }) {
158
+ const description = "OmniVideo: roteiro, motion em código, voz clonada, legenda e publicação, do pedido ao MP4.";
159
+ const [command, ...args] = cli.split(" ");
160
+ /** @type {Record<string, string>} */
161
+ const files = {
162
+ "hooks/hooks.json": `${JSON.stringify(
163
+ {
164
+ hooks: {
165
+ SessionStart: [{ hooks: [{ type: "command", command: `${cli} up --hook`, async: true, timeout: 600 }] }],
166
+ },
167
+ },
168
+ null,
169
+ 2,
170
+ )}\n`,
171
+ ".mcp.json": `${JSON.stringify({ mcpServers: { omnivideo: { command, args: [...args, "mcp"] } } }, null, 2)}\n`,
172
+ "README.md": `# OmniVideo para o Claude Code
173
+
174
+ ${description}
175
+
176
+ claude plugin install omnivideo --marketplace ${owner}/omni-plugin
177
+
178
+ Na primeira sessão o plugin prepara o computador sozinho: abre o navegador para você entrar no OmniVideo e
179
+ liga o studio local. Precisa do Chrome e do ffmpeg instalados. Este repositório é gerado automaticamente:
180
+ as skills chegam do registro do OmniVideo a cada uso.
181
+ `,
182
+ ...Object.fromEntries(Object.entries(release.headers).map(([name, text]) => [`skills/${name}/SKILL.md`, text])),
183
+ };
184
+ const version = `0.1.0-${sha(JSON.stringify(files)).slice(0, 10)}`;
185
+ files[".claude-plugin/plugin.json"] = `${JSON.stringify(
186
+ { name: "omnivideo", version, description, author: { name: "Crievo" }, homepage: "https://omni.crievo.com" },
187
+ null,
188
+ 2,
189
+ )}\n`;
190
+ files[".claude-plugin/marketplace.json"] = `${JSON.stringify(
191
+ { name: "omnivideo", owner: { name: owner }, plugins: [{ name: "omnivideo", source: "./", description, version }] },
192
+ null,
193
+ 2,
194
+ )}\n`;
195
+ return files;
196
+ }
@@ -0,0 +1,52 @@
1
+ /* Test helpers for the core: a fake fetch with canned routes and a fixed clock. Not used at runtime. */
2
+
3
+ /**
4
+ * A fake fetch for tests: each call takes the first route whose `match`
5
+ * (substring or RegExp, plus optional method) fits; records every call.
6
+ * @param {Array<{ match: string | RegExp, method?: string, status?: number, body?: any, headers?: Record<string, string> } | ((url: string, opts: any) => any)>} routes
7
+ */
8
+ export function fakeFetch(routes) {
9
+ /** @type {Array<{ url: string, opts: any }>} */
10
+ const calls = [];
11
+ /** @type {typeof globalThis.fetch & { calls: typeof calls }} */
12
+ const f = /** @type {any} */ (
13
+ async (/** @type {string} */ url, /** @type {any} */ opts = {}) => {
14
+ calls.push({ url: String(url), opts });
15
+ for (const route of routes) {
16
+ const hit =
17
+ typeof route === "function"
18
+ ? route(String(url), opts)
19
+ : (typeof route.match === "string" ? String(url).includes(route.match) : route.match.test(String(url))) &&
20
+ (!route.method || route.method === (opts.method ?? "GET"))
21
+ ? route
22
+ : null;
23
+ if (!hit) continue;
24
+ const status = hit.status ?? 200;
25
+ const text = typeof hit.body === "string" ? hit.body : JSON.stringify(hit.body ?? {});
26
+ return new Response(text, { status, headers: hit.headers ?? {} });
27
+ }
28
+ throw new Error(`fakeFetch: no route for ${opts.method ?? "GET"} ${url}`);
29
+ }
30
+ );
31
+ f.calls = calls;
32
+ return f;
33
+ }
34
+
35
+ /**
36
+ * A clock that only moves when told to.
37
+ * @param {string | number} start ISO date or epoch ms
38
+ */
39
+ export function fixedClock(start) {
40
+ let t = typeof start === "number" ? start : Date.parse(start);
41
+ return {
42
+ now: () => t,
43
+ /** @param {number} ms */
44
+ advance: ms => {
45
+ t += ms;
46
+ },
47
+ /** @param {number} ms */
48
+ sleep: async ms => {
49
+ t += ms;
50
+ },
51
+ };
52
+ }
@@ -0,0 +1,187 @@
1
+ /* `omni up`: everything the CLI needs on this machine, done without the user (docs/specs/phase-2-plugin-cli.md,
2
+ step 4). The plugin's SessionStart hook runs it on every session, so every piece checks first and does
3
+ nothing when it is already there:
4
+
5
+ workspace ~/OmniVideo (OMNI_HOME): videos/, omnivideo.config.json pointing at the hosted studio, .env
6
+ app a stable copy of the package in ~/.omni/app/<version>/ (npx's cache can be wiped), and
7
+ ~/.omni/bin/omni
8
+ python the venv of the voice and timing tools, in the app's .venv (in the background on a hook)
9
+ login none yet: the browser opens on the approval page (at most once an hour)
10
+ service the studio as a launchd agent, from the app, so the hosted studio reaches this Mac
11
+
12
+ In this repo (a git checkout) the app, the venv and the service are left to `tools/setup.mjs` and
13
+ `tools/runner.mjs service`: `up` never rewires a development machine. */
14
+
15
+ import { spawn, spawnSync } from "node:child_process";
16
+ import { copyFileSync, existsSync, mkdirSync, readFileSync, statSync, writeFileSync } from "node:fs";
17
+ import { homedir } from "node:os";
18
+ import { join } from "node:path";
19
+ import { missingRules } from "./claude-settings.mjs";
20
+ import { CLI_COMMAND, PACKAGE, STUDIO_URL } from "./cli.mjs";
21
+ import { readTokens } from "./oauth-client.mjs";
22
+ import { CODE_ROOT, DATA_ROOT } from "./paths.mjs";
23
+ import { PLIST, service, serviceInstall } from "./service.mjs";
24
+
25
+ export const OMNI_DIR = join(homedir(), ".omni");
26
+ const LOGIN_EVERY_MS = 60 * 60_000;
27
+
28
+ const isDev = (/** @type {string} */ codeRoot) => existsSync(join(codeRoot, ".git"));
29
+
30
+ /** the studio URL of a workspace's config, else the default */
31
+ export function studioUrlOf(home = DATA_ROOT) {
32
+ try {
33
+ const c = JSON.parse(readFileSync(join(home, "omnivideo.config.json"), "utf8"));
34
+ return String(c.cloud?.studio_url || STUDIO_URL).replace(/\/+$/, "");
35
+ } catch {
36
+ return STUDIO_URL;
37
+ }
38
+ }
39
+
40
+ /** videos/, a config that talks to the hosted studio, and .env, from the package's examples
41
+ * @returns {boolean} whether it created anything */
42
+ export function ensureWorkspace(home = DATA_ROOT, codeRoot = CODE_ROOT) {
43
+ let created = false;
44
+ if (!existsSync(join(home, "videos"))) {
45
+ mkdirSync(join(home, "videos"), { recursive: true });
46
+ created = true;
47
+ }
48
+ const config = join(home, "omnivideo.config.json");
49
+ if (!existsSync(config)) {
50
+ const c = JSON.parse(readFileSync(join(codeRoot, "omnivideo.config.example.json"), "utf8"));
51
+ c.cloud = { ...(c.cloud ?? {}), studio: true, studio_url: STUDIO_URL };
52
+ writeFileSync(config, `${JSON.stringify(c, null, 2)}\n`);
53
+ created = true;
54
+ }
55
+ const env = join(home, ".env");
56
+ if (!existsSync(env) && existsSync(join(codeRoot, ".env.example"))) {
57
+ copyFileSync(join(codeRoot, ".env.example"), env);
58
+ created = true;
59
+ }
60
+ return created;
61
+ }
62
+
63
+ /** The code root the service should run: this checkout, an already stable install, or a fresh copy in
64
+ * ~/.omni/app/<version>/ when this runs from npx's cache. @param {(line: string) => void} log */
65
+ export function ensureApp(log = () => {}, codeRoot = CODE_ROOT) {
66
+ if (isDev(codeRoot) || !codeRoot.includes("/_npx/")) return codeRoot;
67
+ const version = JSON.parse(readFileSync(join(codeRoot, "package.json"), "utf8")).version;
68
+ const prefix = join(OMNI_DIR, "app", version);
69
+ const installed = join(prefix, "node_modules", ...PACKAGE.split("/"));
70
+ if (!existsSync(join(installed, "package.json"))) {
71
+ log(`instalando o OmniVideo ${version} em ${prefix}…`);
72
+ const spec = process.env.OMNI_PACKAGE_SPEC || `${PACKAGE}@${version}`;
73
+ const r = spawnSync("npm", ["install", "--prefix", prefix, "--no-audit", "--no-fund", "--loglevel=error", spec], {
74
+ encoding: "utf8",
75
+ });
76
+ if (r.status !== 0) throw new Error(`npm install falhou: ${(r.stderr || r.stdout).trim().split("\n").at(-1)}`);
77
+ }
78
+ mkdirSync(join(OMNI_DIR, "bin"), { recursive: true });
79
+ writeFileSync(join(OMNI_DIR, "bin", "omni"), `#!/bin/sh\nexec node "${join(installed, "bin", "omni.mjs")}" "$@"\n`, {
80
+ mode: 0o755,
81
+ });
82
+ return installed;
83
+ }
84
+
85
+ /** the venv of the Python tools, inside the app; on a hook in the background, logging to ~/.omni/venv.log
86
+ * @returns {"ok" | "started" | "creating" | "failed"} */
87
+ export function ensureVenv(codeRoot = CODE_ROOT, { background = false } = {}) {
88
+ if (existsSync(join(codeRoot, ".venv", "bin", "python"))) return "ok";
89
+ const lock = join(OMNI_DIR, "venv.lock");
90
+ mkdirSync(OMNI_DIR, { recursive: true });
91
+ try {
92
+ if (Date.now() - statSync(lock).mtimeMs < 15 * 60_000) return "creating";
93
+ } catch {
94
+ /* no one is creating it */
95
+ }
96
+ writeFileSync(lock, String(process.pid));
97
+ const script = `python3 -m venv .venv && .venv/bin/python -m pip install -q -r requirements.txt; rm -f "${lock}"`;
98
+ if (background) {
99
+ const out = join(OMNI_DIR, "venv.log");
100
+ spawn("/bin/sh", ["-c", `${script} >> "${out}" 2>&1`], { cwd: codeRoot, detached: true, stdio: "ignore" }).unref();
101
+ return "started";
102
+ }
103
+ const r = spawnSync("/bin/sh", ["-c", script], { cwd: codeRoot, stdio: "inherit" });
104
+ return r.status === 0 ? "ok" : "failed";
105
+ }
106
+
107
+ /** opens the browser on the approval page in a process of its own, at most once an hour
108
+ * @returns {boolean} whether it opened now */
109
+ export function startLogin(codeRoot = CODE_ROOT) {
110
+ const marker = join(OMNI_DIR, "login-opened");
111
+ mkdirSync(OMNI_DIR, { recursive: true });
112
+ try {
113
+ if (Date.now() - statSync(marker).mtimeMs < LOGIN_EVERY_MS) return false;
114
+ } catch {
115
+ /* never opened */
116
+ }
117
+ writeFileSync(marker, new Date().toISOString());
118
+ spawn(process.execPath, [join(codeRoot, "tools", "omni.mjs"), "login"], {
119
+ detached: true,
120
+ stdio: "ignore",
121
+ env: { ...process.env, OMNI_HOME: DATA_ROOT },
122
+ }).unref();
123
+ return true;
124
+ }
125
+
126
+ /** the checkout the installed studio service runs from, when it is a git checkout (a development machine) */
127
+ function devServiceRoot() {
128
+ try {
129
+ const wd = /<key>WorkingDirectory<\/key><string>([^<]+)<\/string>/.exec(readFileSync(PLIST, "utf8"))?.[1];
130
+ const root = wd ? join(wd.replace(/&amp;/g, "&"), "..") : "";
131
+ return root && existsSync(join(root, ".git")) ? root : "";
132
+ } catch {
133
+ return "";
134
+ }
135
+ }
136
+
137
+ /**
138
+ * @param {{ hook?: boolean, log?: (line: string) => void }} [o]
139
+ * @returns {Promise<{ line: string, codeRoot: string, loggedIn: boolean, missingRules: string[] }>}
140
+ */
141
+ export async function up({ hook = false, log = () => {} } = {}) {
142
+ const notes = [];
143
+ if (ensureWorkspace()) notes.push(`workspace criado em ${DATA_ROOT}`);
144
+ const codeRoot = ensureApp(log);
145
+ const dev = isDev(codeRoot);
146
+ const url = studioUrlOf();
147
+
148
+ if (!dev) {
149
+ const venv = ensureVenv(codeRoot, { background: hook });
150
+ if (venv === "started" || venv === "creating") notes.push("preparando o Python das ferramentas de voz");
151
+ if (venv === "failed") notes.push("o Python das ferramentas não instalou (veja ~/.omni/venv.log)");
152
+ }
153
+
154
+ const loggedIn = !!readTokens(url);
155
+ if (!loggedIn)
156
+ notes.push(
157
+ startLogin(codeRoot) ? "abri o navegador para entrar no OmniVideo" : "falta entrar no OmniVideo (omni login)",
158
+ );
159
+
160
+ /* a development studio already kept on by launchd (this repo's runner.mjs) is never swapped for the app */
161
+ const devService = devServiceRoot();
162
+ if (!dev && devService)
163
+ notes.push(`o studio de desenvolvimento (${devService}) continua sendo o serviço deste computador`);
164
+ if (!dev && !devService && process.platform === "darwin") {
165
+ try {
166
+ if (serviceInstall({ codeRoot, onlyIfChanged: true, log }).changed) notes.push("studio ligado como serviço");
167
+ } catch (e) {
168
+ notes.push(`o serviço do studio não ligou: ${/** @type {Error} */ (e).message}`);
169
+ }
170
+ }
171
+
172
+ const rules = missingRules({ cli: CLI_COMMAND });
173
+ if (rules.length)
174
+ notes.push("as skills ainda precisam de permissão: peça ao Claude para liberar as skills do OmniVideo");
175
+
176
+ const s = service();
177
+ const state = [
178
+ loggedIn ? "logado" : "sem login",
179
+ dev ? "modo desenvolvimento" : s.loaded ? "studio ligado" : "studio desligado",
180
+ ];
181
+ return {
182
+ line: `OmniVideo: ${state.join(", ")}${notes.length ? `. ${notes.join("; ")}.` : "."}`,
183
+ codeRoot,
184
+ loggedIn,
185
+ missingRules: rules,
186
+ };
187
+ }