@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.
- package/.env.example +41 -0
- package/README.md +2 -2
- package/bin/omni.mjs +18 -0
- package/docs/onboarding/clone-voice.png +0 -0
- package/docs/onboarding/getting-started.png +0 -0
- package/docs/onboarding/new-video.png +0 -0
- package/docs/onboarding/providers.png +0 -0
- package/docs/onboarding/publish.png +0 -0
- package/docs/onboarding/references.png +0 -0
- package/docs/onboarding.md +212 -0
- package/omnivideo.config.example.json +437 -0
- package/package.json +40 -4
- package/reference/captions.md +156 -0
- package/reference/carousel-contract.md +91 -0
- package/reference/cloud.md +96 -0
- package/reference/learnings.md +792 -0
- package/reference/omnichat.md +305 -0
- package/reference/premiere-mcp.md +91 -0
- package/reference/publish.md +305 -0
- package/reference/scenes-contract.md +109 -0
- package/reference/studio.md +666 -0
- package/reference/voicestudio.md +81 -0
- package/reference/writing-for-voice.md +67 -0
- package/requirements.txt +3 -0
- package/studio/api/app.ts +87 -0
- package/studio/api/lib/errors.ts +33 -0
- package/studio/api/lib/files.ts +41 -0
- package/studio/api/lib/hono.ts +9 -0
- package/studio/api/lib/result.ts +14 -0
- package/studio/api/lib/validate.ts +31 -0
- package/studio/api/lib/video.ts +10 -0
- package/studio/api/middleware/json-charset.ts +11 -0
- package/studio/api/middleware/local-only.ts +32 -0
- package/studio/api/middleware/no-timeout.ts +10 -0
- package/studio/api/routes/admin.ts +18 -0
- package/studio/api/routes/agent-cascade.ts +100 -0
- package/studio/api/routes/agent-history.ts +23 -0
- package/studio/api/routes/agent.ts +162 -0
- package/studio/api/routes/analytics.ts +31 -0
- package/studio/api/routes/approval.ts +60 -0
- package/studio/api/routes/archive.ts +12 -0
- package/studio/api/routes/captions.ts +28 -0
- package/studio/api/routes/carousel.ts +38 -0
- package/studio/api/routes/clone.ts +27 -0
- package/studio/api/routes/create.ts +19 -0
- package/studio/api/routes/events.ts +11 -0
- package/studio/api/routes/export.ts +14 -0
- package/studio/api/routes/media.ts +30 -0
- package/studio/api/routes/omnichat.ts +113 -0
- package/studio/api/routes/onboarding.ts +14 -0
- package/studio/api/routes/publish.ts +40 -0
- package/studio/api/routes/queue.ts +14 -0
- package/studio/api/routes/references.ts +40 -0
- package/studio/api/routes/runner.ts +17 -0
- package/studio/api/routes/schedule.ts +47 -0
- package/studio/api/routes/services.ts +11 -0
- package/studio/api/routes/sessions.ts +27 -0
- package/studio/api/routes/settings.ts +102 -0
- package/studio/api/routes/sound.ts +45 -0
- package/studio/api/routes/thumbnail.ts +18 -0
- package/studio/api/routes/usage.ts +13 -0
- package/studio/api/routes/videos.ts +41 -0
- package/studio/api/routes/voices.ts +71 -0
- package/studio/api/services/admin.ts +113 -0
- package/studio/api/services/agent-cascade.ts +975 -0
- package/studio/api/services/agent-claude.ts +47 -0
- package/studio/api/services/agent-history.ts +103 -0
- package/studio/api/services/agent-persona.ts +77 -0
- package/studio/api/services/agent-tools.ts +2237 -0
- package/studio/api/services/agent-ui-tools.ts +259 -0
- package/studio/api/services/agent-wait.ts +32 -0
- package/studio/api/services/agent.ts +553 -0
- package/studio/api/services/analytics.ts +92 -0
- package/studio/api/services/approval.ts +134 -0
- package/studio/api/services/archive.ts +38 -0
- package/studio/api/services/captions.ts +268 -0
- package/studio/api/services/carousel.ts +206 -0
- package/studio/api/services/classify.ts +196 -0
- package/studio/api/services/clone-phrases.ts +75 -0
- package/studio/api/services/clone.ts +722 -0
- package/studio/api/services/create.ts +421 -0
- package/studio/api/services/events.ts +215 -0
- package/studio/api/services/media.ts +32 -0
- package/studio/api/services/omnichat.ts +624 -0
- package/studio/api/services/onboarding.ts +77 -0
- package/studio/api/services/publish.ts +287 -0
- package/studio/api/services/queue.ts +150 -0
- package/studio/api/services/references.ts +269 -0
- package/studio/api/services/root.ts +60 -0
- package/studio/api/services/services.ts +114 -0
- package/studio/api/services/sessions.ts +1007 -0
- package/studio/api/services/settings.ts +983 -0
- package/studio/api/services/sound.ts +311 -0
- package/studio/api/services/thumbnail.ts +133 -0
- package/studio/api/services/usage.ts +27 -0
- package/studio/api/services/videos.ts +418 -0
- package/studio/api/services/voice-pause.ts +98 -0
- package/studio/api/services/voices.ts +324 -0
- package/studio/runner/runner.ts +244 -0
- package/studio/shared/omni-actions.ts +68 -0
- package/studio/shared/omni-admin.ts +52 -0
- package/studio/shared/omni-farewell.ts +48 -0
- package/studio/shared/omni-hotword.ts +20 -0
- package/studio/shared/omni-screens.ts +228 -0
- package/studio/shared/schemas/admin.ts +15 -0
- package/studio/shared/schemas/agent-cascade.ts +58 -0
- package/studio/shared/schemas/agent-history.ts +73 -0
- package/studio/shared/schemas/agent.ts +50 -0
- package/studio/shared/schemas/analytics.ts +6 -0
- package/studio/shared/schemas/approval.ts +22 -0
- package/studio/shared/schemas/captions.ts +19 -0
- package/studio/shared/schemas/carousel.ts +26 -0
- package/studio/shared/schemas/clone.ts +40 -0
- package/studio/shared/schemas/common.ts +16 -0
- package/studio/shared/schemas/omnichat.ts +54 -0
- package/studio/shared/schemas/onboarding.ts +5 -0
- package/studio/shared/schemas/publish.ts +45 -0
- package/studio/shared/schemas/references.ts +20 -0
- package/studio/shared/schemas/settings.ts +82 -0
- package/studio/shared/schemas/sound.ts +22 -0
- package/studio/shared/schemas/videos.ts +38 -0
- package/studio/shared/schemas/voices.ts +23 -0
- package/studio/shared/types/api.ts +1290 -0
- package/studio/studio.ts +148 -0
- package/studio/tsconfig.json +15 -0
- package/studio/web/assets/logos/cartesia.svg +4 -0
- package/studio/web/assets/logos/elevenlabs.svg +12 -0
- package/studio/web/assets/logos/fish.svg +24 -0
- package/studio/web/assets/logos/omni-black.png +0 -0
- package/studio/web/assets/logos/omni-white.png +0 -0
- package/studio/web/assets/logos/voicestudio.svg +21 -0
- package/studio/web/components/AgentMark.tsx +7 -0
- package/studio/web/components/AgentWalking.tsx +11 -0
- package/studio/web/components/ApiKeyField.tsx +104 -0
- package/studio/web/components/AppShell.tsx +64 -0
- package/studio/web/components/ApprovalNotice.tsx +65 -0
- package/studio/web/components/AvatarGroupIcon.tsx +22 -0
- package/studio/web/components/AvatarNameField.tsx +86 -0
- package/studio/web/components/AvatarPicker.tsx +84 -0
- package/studio/web/components/BrandLogo.tsx +16 -0
- package/studio/web/components/CartesiaPlanSetting.tsx +90 -0
- package/studio/web/components/ClaudeWalking.tsx +32 -0
- package/studio/web/components/CloneVoiceDialog/GuidedScript.tsx +39 -0
- package/studio/web/components/CloneVoiceDialog/LanguageBar.tsx +37 -0
- package/studio/web/components/CloneVoiceDialog/LevelMeter.tsx +31 -0
- package/studio/web/components/CloneVoiceDialog/ProviderChoice.tsx +81 -0
- package/studio/web/components/CloneVoiceDialog/QualityPanel.tsx +62 -0
- package/studio/web/components/CloneVoiceDialog/RecordStep.tsx +50 -0
- package/studio/web/components/CloneVoiceDialog/RecorderBar.tsx +79 -0
- package/studio/web/components/CloneVoiceDialog/RedoSummary.tsx +54 -0
- package/studio/web/components/CloneVoiceDialog/SaveStep.tsx +91 -0
- package/studio/web/components/CloneVoiceDialog/SourceBar.tsx +30 -0
- package/studio/web/components/CloneVoiceDialog/index.tsx +141 -0
- package/studio/web/components/CloneVoiceDialog/types.ts +28 -0
- package/studio/web/components/CloneVoiceDialog/useCloneVoice.ts +528 -0
- package/studio/web/components/ConnectionSignals.tsx +51 -0
- package/studio/web/components/EditVoiceDialog/EditVoiceFooter.tsx +72 -0
- package/studio/web/components/EditVoiceDialog/ProviderCards.tsx +81 -0
- package/studio/web/components/EditVoiceDialog/index.tsx +256 -0
- package/studio/web/components/ErrorBanner.tsx +115 -0
- package/studio/web/components/FinishedTask.tsx +41 -0
- package/studio/web/components/FishModelSetting.tsx +69 -0
- package/studio/web/components/FishPriceSetting.tsx +44 -0
- package/studio/web/components/HumanBanner.tsx +64 -0
- package/studio/web/components/Lightbox.tsx +41 -0
- package/studio/web/components/LogStream.tsx +42 -0
- package/studio/web/components/Markdown.tsx +21 -0
- package/studio/web/components/MissingDmsPanel/BatchSend.tsx +137 -0
- package/studio/web/components/MissingDmsPanel/CommentCard.tsx +168 -0
- package/studio/web/components/MissingDmsPanel/CommentFeed.tsx +45 -0
- package/studio/web/components/MissingDmsPanel/DmBubble.tsx +38 -0
- package/studio/web/components/MissingDmsPanel/ReplyComposer.tsx +63 -0
- package/studio/web/components/MissingDmsPanel/index.tsx +98 -0
- package/studio/web/components/NotFound.tsx +48 -0
- package/studio/web/components/Omni/OmniAvatarPicker.tsx +92 -0
- package/studio/web/components/Omni/OmniCards.tsx +202 -0
- package/studio/web/components/Omni/OmniGlow.tsx +183 -0
- package/studio/web/components/Omni/OmniHistory.tsx +119 -0
- package/studio/web/components/Omni/OmniHotword.tsx +40 -0
- package/studio/web/components/Omni/OmniMicSelect.tsx +92 -0
- package/studio/web/components/Omni/OmniVoiceSample.tsx +81 -0
- package/studio/web/components/Omni/OmniWidget.tsx +743 -0
- package/studio/web/components/Omni/PendingActions.tsx +200 -0
- package/studio/web/components/Omni/Transcript.tsx +127 -0
- package/studio/web/components/OmnichatAccountStatus.tsx +87 -0
- package/studio/web/components/OmnichatRulePanel/RuleConfig.tsx +150 -0
- package/studio/web/components/OmnichatRulePanel/RuleDryRun.tsx +15 -0
- package/studio/web/components/OmnichatRulePanel/RuleEnableConfirm.tsx +42 -0
- package/studio/web/components/OmnichatRulePanel/RuleSkeleton.tsx +18 -0
- package/studio/web/components/OmnichatRulePanel/RuleStatus.tsx +55 -0
- package/studio/web/components/OmnichatRulePanel/index.tsx +115 -0
- package/studio/web/components/OmnichatRulePanel/rule-steps.ts +67 -0
- package/studio/web/components/OmnichatRulePanel/steps/MessageStep.tsx +91 -0
- package/studio/web/components/OmnichatRulePanel/steps/RepliesStep.tsx +201 -0
- package/studio/web/components/OmnichatRulePanel/steps/TriggerStep.tsx +76 -0
- package/studio/web/components/OmnichatRulePanel/steps/types.ts +11 -0
- package/studio/web/components/Palette.tsx +17 -0
- package/studio/web/components/PlatformCard.tsx +109 -0
- package/studio/web/components/PlatformIcon.tsx +19 -0
- package/studio/web/components/ProviderCard.tsx +80 -0
- package/studio/web/components/ProviderDetail.tsx +367 -0
- package/studio/web/components/ProviderLogo.tsx +20 -0
- package/studio/web/components/QueuePanel.tsx +77 -0
- package/studio/web/components/ReferenceActions.tsx +89 -0
- package/studio/web/components/ReferencePicker.tsx +136 -0
- package/studio/web/components/ResultMessage.tsx +7 -0
- package/studio/web/components/RuleForm.tsx +138 -0
- package/studio/web/components/RuleLog.tsx +151 -0
- package/studio/web/components/RunningTask.tsx +23 -0
- package/studio/web/components/SampleList.tsx +55 -0
- package/studio/web/components/SessionPanel.tsx +352 -0
- package/studio/web/components/SettingRow.tsx +14 -0
- package/studio/web/components/SettingsCategorySkeleton.tsx +25 -0
- package/studio/web/components/SidePanel.tsx +77 -0
- package/studio/web/components/Sidebar.tsx +289 -0
- package/studio/web/components/StageStepper.tsx +87 -0
- package/studio/web/components/StudioOffline.tsx +80 -0
- package/studio/web/components/TaskName.tsx +17 -0
- package/studio/web/components/TopBanners.tsx +30 -0
- package/studio/web/components/VideoOmnichatPanel.tsx +287 -0
- package/studio/web/components/VoiceAvatar.tsx +33 -0
- package/studio/web/components/VoiceCarousel.tsx +196 -0
- package/studio/web/components/VoiceCategory.tsx +9 -0
- package/studio/web/components/VoiceModal.tsx +39 -0
- package/studio/web/components/VoiceProfileCard.tsx +75 -0
- package/studio/web/components/VoiceProfilePicker.tsx +107 -0
- package/studio/web/components/VoiceProfilesSettings.tsx +150 -0
- package/studio/web/components/VoiceProviderCard.tsx +118 -0
- package/studio/web/components/VoiceProvidersCard.tsx +26 -0
- package/studio/web/components/WaitList.tsx +21 -0
- package/studio/web/components/icons.tsx +247 -0
- package/studio/web/components/ui/accordion.tsx +47 -0
- package/studio/web/components/ui/badge.tsx +18 -0
- package/studio/web/components/ui/calendar.tsx +43 -0
- package/studio/web/components/ui/chart.tsx +229 -0
- package/studio/web/components/ui/checkbox.tsx +26 -0
- package/studio/web/components/ui/cx.ts +2 -0
- package/studio/web/components/ui/date-time-picker.tsx +84 -0
- package/studio/web/components/ui/panel.tsx +23 -0
- package/studio/web/components/ui/picker.tsx +58 -0
- package/studio/web/components/ui/popover.tsx +24 -0
- package/studio/web/components/ui/select.tsx +89 -0
- package/studio/web/components/ui/skeleton.tsx +10 -0
- package/studio/web/components/ui/slider.tsx +68 -0
- package/studio/web/components/ui/switch.tsx +13 -0
- package/studio/web/components/ui/tooltip.tsx +59 -0
- package/studio/web/hooks/useAccounts.ts +27 -0
- package/studio/web/hooks/useAdmin.ts +12 -0
- package/studio/web/hooks/useAnalytics.ts +21 -0
- package/studio/web/hooks/useAvatarAudioReactive.ts +87 -0
- package/studio/web/hooks/useBrowserConnect.ts +48 -0
- package/studio/web/hooks/useClonePlan.ts +14 -0
- package/studio/web/hooks/useConnected.ts +7 -0
- package/studio/web/hooks/useLiveQuery.ts +29 -0
- package/studio/web/hooks/useMenuCollapsed.ts +32 -0
- package/studio/web/hooks/useMicDevices.ts +53 -0
- package/studio/web/hooks/useMissingDms.ts +211 -0
- package/studio/web/hooks/useMissingSetupSteps.ts +26 -0
- package/studio/web/hooks/useNow.ts +11 -0
- package/studio/web/hooks/useOmniAgent.ts +278 -0
- package/studio/web/hooks/useOmniClaude.ts +124 -0
- package/studio/web/hooks/useOmniDock.ts +142 -0
- package/studio/web/hooks/useOmniGreeting.ts +60 -0
- package/studio/web/hooks/useOmniHotword.ts +128 -0
- package/studio/web/hooks/useOmnichat.ts +26 -0
- package/studio/web/hooks/useOnboarding.ts +27 -0
- package/studio/web/hooks/usePost.ts +23 -0
- package/studio/web/hooks/useQueue.ts +10 -0
- package/studio/web/hooks/useReferences.ts +20 -0
- package/studio/web/hooks/useRuleEditor.ts +101 -0
- package/studio/web/hooks/useRunner.ts +24 -0
- package/studio/web/hooks/useSaveAction.ts +23 -0
- package/studio/web/hooks/useSchedule.ts +11 -0
- package/studio/web/hooks/useServices.ts +10 -0
- package/studio/web/hooks/useSessions.ts +15 -0
- package/studio/web/hooks/useSettings.ts +11 -0
- package/studio/web/hooks/useSettingsSection.ts +30 -0
- package/studio/web/hooks/useSidePanel.ts +23 -0
- package/studio/web/hooks/useSound.ts +30 -0
- package/studio/web/hooks/useUsage.ts +11 -0
- package/studio/web/hooks/useVideoVoice.ts +36 -0
- package/studio/web/hooks/useVideos.ts +21 -0
- package/studio/web/hooks/useVoiceProfilePreview.ts +52 -0
- package/studio/web/hooks/useVoiceProfiles.ts +7 -0
- package/studio/web/hooks/useVoiceRunning.ts +10 -0
- package/studio/web/hooks/useVoiceStudioClosed.ts +8 -0
- package/studio/web/index.html +19 -0
- package/studio/web/lib/agent.ts +7 -0
- package/studio/web/lib/alerts.ts +210 -0
- package/studio/web/lib/analytics-metrics.ts +351 -0
- package/studio/web/lib/api.ts +70 -0
- package/studio/web/lib/audio.ts +39 -0
- package/studio/web/lib/avatars.ts +41 -0
- package/studio/web/lib/calendar.ts +218 -0
- package/studio/web/lib/caption-position.ts +249 -0
- package/studio/web/lib/carousel.ts +24 -0
- package/studio/web/lib/chime.ts +42 -0
- package/studio/web/lib/clone-voice.ts +55 -0
- package/studio/web/lib/export.ts +11 -0
- package/studio/web/lib/files.ts +8 -0
- package/studio/web/lib/format.ts +93 -0
- package/studio/web/lib/media.ts +29 -0
- package/studio/web/lib/mic-device.ts +72 -0
- package/studio/web/lib/missing-dms.ts +70 -0
- package/studio/web/lib/omni-cards.ts +42 -0
- package/studio/web/lib/omni-cascade.ts +1067 -0
- package/studio/web/lib/omni-greeting-audio.ts +24 -0
- package/studio/web/lib/omni-hotword.ts +231 -0
- package/studio/web/lib/omni-panel.ts +67 -0
- package/studio/web/lib/omni-realtime.ts +811 -0
- package/studio/web/lib/omni-ui.ts +402 -0
- package/studio/web/lib/omni-wait.ts +174 -0
- package/studio/web/lib/omnichat-api.ts +44 -0
- package/studio/web/lib/orb.ts +270 -0
- package/studio/web/lib/pcm-recorder.ts +115 -0
- package/studio/web/lib/platforms.ts +17 -0
- package/studio/web/lib/providers.ts +27 -0
- package/studio/web/lib/publish-draft.ts +52 -0
- package/studio/web/lib/publish-settings.ts +81 -0
- package/studio/web/lib/query-client.ts +122 -0
- package/studio/web/lib/references.ts +13 -0
- package/studio/web/lib/services.ts +40 -0
- package/studio/web/lib/sessions.ts +103 -0
- package/studio/web/lib/settings-categories.ts +58 -0
- package/studio/web/lib/sound-audio.ts +33 -0
- package/studio/web/lib/sound-timeline.ts +291 -0
- package/studio/web/lib/sse.ts +86 -0
- package/studio/web/lib/stage-history.ts +141 -0
- package/studio/web/lib/stages.ts +344 -0
- package/studio/web/lib/studio-offline.ts +60 -0
- package/studio/web/lib/upload.ts +24 -0
- package/studio/web/lib/video-tabs.ts +118 -0
- package/studio/web/lib/voice-profile-draft.ts +34 -0
- package/studio/web/lib/voice-profiles.ts +3 -0
- package/studio/web/main.tsx +14 -0
- package/studio/web/pages/AdminPage/index.tsx +123 -0
- package/studio/web/pages/AgentPage/components/Measurements.tsx +102 -0
- package/studio/web/pages/AgentPage/index.tsx +28 -0
- package/studio/web/pages/AnalyticsPage/components/About.tsx +52 -0
- package/studio/web/pages/AnalyticsPage/components/ChannelCards.tsx +106 -0
- package/studio/web/pages/AnalyticsPage/components/ChannelView.tsx +137 -0
- package/studio/web/pages/AnalyticsPage/components/CollectionStatus.tsx +46 -0
- package/studio/web/pages/AnalyticsPage/components/Comparison.tsx +68 -0
- package/studio/web/pages/AnalyticsPage/components/Diagnosis.tsx +50 -0
- package/studio/web/pages/AnalyticsPage/components/Highlights.tsx +49 -0
- package/studio/web/pages/AnalyticsPage/components/Hint.tsx +11 -0
- package/studio/web/pages/AnalyticsPage/components/HookVsBody.tsx +100 -0
- package/studio/web/pages/AnalyticsPage/components/HourlyWave.tsx +133 -0
- package/studio/web/pages/AnalyticsPage/components/Insights.tsx +70 -0
- package/studio/web/pages/AnalyticsPage/components/MetricCard.tsx +46 -0
- package/studio/web/pages/AnalyticsPage/components/PlanSection.tsx +40 -0
- package/studio/web/pages/AnalyticsPage/components/ProgressionChart.tsx +155 -0
- package/studio/web/pages/AnalyticsPage/components/ReelBarsChart.tsx +154 -0
- package/studio/web/pages/AnalyticsPage/components/ReelThumb.tsx +19 -0
- package/studio/web/pages/AnalyticsPage/components/ReelView.tsx +290 -0
- package/studio/web/pages/AnalyticsPage/components/ReelsTable.tsx +269 -0
- package/studio/web/pages/AnalyticsPage/components/RequestWithFocus.tsx +38 -0
- package/studio/web/pages/AnalyticsPage/components/SegmentedControl.tsx +35 -0
- package/studio/web/pages/AnalyticsPage/components/VsMedian.tsx +31 -0
- package/studio/web/pages/AnalyticsPage/index.tsx +17 -0
- package/studio/web/pages/GettingStartedPage/index.tsx +174 -0
- package/studio/web/pages/NewVideoPage/components/DeliverablesPicker.tsx +62 -0
- package/studio/web/pages/NewVideoPage/components/FormatPicker.tsx +33 -0
- package/studio/web/pages/NewVideoPage/index.tsx +133 -0
- package/studio/web/pages/NotFoundPage/index.tsx +9 -0
- package/studio/web/pages/OmnichatPage/components/NewFromLink.tsx +59 -0
- package/studio/web/pages/OmnichatPage/components/RuleListItem.tsx +64 -0
- package/studio/web/pages/OmnichatPage/components/RulePreview.tsx +14 -0
- package/studio/web/pages/OmnichatPage/components/RuleView.tsx +88 -0
- package/studio/web/pages/OmnichatPage/index.tsx +153 -0
- package/studio/web/pages/QueuePage/index.tsx +22 -0
- package/studio/web/pages/ReferencePage/components/ReferenceTexts.tsx +131 -0
- package/studio/web/pages/ReferencePage/index.tsx +216 -0
- package/studio/web/pages/ReferencesPage/components/NewReferenceForm.tsx +150 -0
- package/studio/web/pages/ReferencesPage/components/ReferenceCard.tsx +42 -0
- package/studio/web/pages/ReferencesPage/components/ReferenceGridSkeleton.tsx +31 -0
- package/studio/web/pages/ReferencesPage/index.tsx +67 -0
- package/studio/web/pages/SchedulePage/components/CalendarSkeleton.tsx +18 -0
- package/studio/web/pages/SchedulePage/components/DayView.tsx +177 -0
- package/studio/web/pages/SchedulePage/components/HourPicker.tsx +103 -0
- package/studio/web/pages/SchedulePage/components/MonthDayCell.tsx +111 -0
- package/studio/web/pages/SchedulePage/components/MonthView.tsx +47 -0
- package/studio/web/pages/SchedulePage/components/PlatformIcons.tsx +12 -0
- package/studio/web/pages/SchedulePage/components/PostThumb.tsx +15 -0
- package/studio/web/pages/SchedulePage/components/WeekColumn.tsx +75 -0
- package/studio/web/pages/SchedulePage/components/WeekView.tsx +35 -0
- package/studio/web/pages/SchedulePage/index.tsx +285 -0
- package/studio/web/pages/SettingsPage/components/AgentAccounts.tsx +263 -0
- package/studio/web/pages/SettingsPage/components/AgentCategory.tsx +20 -0
- package/studio/web/pages/SettingsPage/components/ModelPicker.tsx +49 -0
- package/studio/web/pages/SettingsPage/components/ModelsSettings.tsx +138 -0
- package/studio/web/pages/SettingsPage/components/PlatformsCategory.tsx +23 -0
- package/studio/web/pages/SettingsPage/components/PublishingCategory/AccountStatus.tsx +29 -0
- package/studio/web/pages/SettingsPage/components/PublishingCategory/AutoPublishPanel.tsx +90 -0
- package/studio/web/pages/SettingsPage/components/PublishingCategory/BrowserConnect.tsx +62 -0
- package/studio/web/pages/SettingsPage/components/PublishingCategory/CheckField.tsx +19 -0
- package/studio/web/pages/SettingsPage/components/PublishingCategory/InstagramPanel.tsx +113 -0
- package/studio/web/pages/SettingsPage/components/PublishingCategory/OmnichatSettingsPanel.tsx +146 -0
- package/studio/web/pages/SettingsPage/components/PublishingCategory/SaveBar.tsx +33 -0
- package/studio/web/pages/SettingsPage/components/PublishingCategory/SchedulePanel.tsx +132 -0
- package/studio/web/pages/SettingsPage/components/PublishingCategory/SettingsCard.tsx +47 -0
- package/studio/web/pages/SettingsPage/components/PublishingCategory/SettingsCardGroup.tsx +19 -0
- package/studio/web/pages/SettingsPage/components/PublishingCategory/TextPanel.tsx +55 -0
- package/studio/web/pages/SettingsPage/components/PublishingCategory/TiktokPanel.tsx +123 -0
- package/studio/web/pages/SettingsPage/components/PublishingCategory/YoutubePanel.tsx +100 -0
- package/studio/web/pages/SettingsPage/components/PublishingCategory/badges.tsx +15 -0
- package/studio/web/pages/SettingsPage/components/PublishingCategory/index.tsx +40 -0
- package/studio/web/pages/SettingsPage/components/PublishingCategory/types.ts +10 -0
- package/studio/web/pages/SettingsPage/components/UsageCategory.tsx +389 -0
- package/studio/web/pages/SettingsPage/components/VoiceProvidersCategory.tsx +97 -0
- package/studio/web/pages/SettingsPage/index.tsx +85 -0
- package/studio/web/pages/VideoPage/components/AudioPlayer.tsx +113 -0
- package/studio/web/pages/VideoPage/components/CaptionPositionEditor/AxisField.tsx +50 -0
- package/studio/web/pages/VideoPage/components/CaptionPositionEditor/CaptionList.tsx +56 -0
- package/studio/web/pages/VideoPage/components/CaptionPositionEditor/CaptionListItem.tsx +92 -0
- package/studio/web/pages/VideoPage/components/CaptionPositionEditor/CaptionMargins.tsx +57 -0
- package/studio/web/pages/VideoPage/components/CaptionPositionEditor/CaptionStyleFields.tsx +70 -0
- package/studio/web/pages/VideoPage/components/CaptionPositionEditor/CaptionTextForm.tsx +87 -0
- package/studio/web/pages/VideoPage/components/CaptionPositionEditor/ColorField.tsx +37 -0
- package/studio/web/pages/VideoPage/components/CaptionPositionEditor/MarginField.tsx +59 -0
- package/studio/web/pages/VideoPage/components/CaptionPositionEditor/NumberField.tsx +57 -0
- package/studio/web/pages/VideoPage/components/CaptionPositionEditor/PositionControls.tsx +140 -0
- package/studio/web/pages/VideoPage/components/CaptionPositionEditor/captionEditor.ts +215 -0
- package/studio/web/pages/VideoPage/components/CaptionPositionEditor/useCaptionPosition.ts +154 -0
- package/studio/web/pages/VideoPage/components/CarouselChangeRequest.tsx +72 -0
- package/studio/web/pages/VideoPage/components/CarouselCompositionStage.tsx +80 -0
- package/studio/web/pages/VideoPage/components/CarouselCreate.tsx +63 -0
- package/studio/web/pages/VideoPage/components/CarouselMissing.tsx +19 -0
- package/studio/web/pages/VideoPage/components/CarouselPublishStage.tsx +173 -0
- package/studio/web/pages/VideoPage/components/CarouselScriptStage.tsx +82 -0
- package/studio/web/pages/VideoPage/components/CarouselSlideCard.tsx +49 -0
- package/studio/web/pages/VideoPage/components/CarouselViewer.tsx +55 -0
- package/studio/web/pages/VideoPage/components/ChangedVoices.tsx +53 -0
- package/studio/web/pages/VideoPage/components/CompositionStage.tsx +78 -0
- package/studio/web/pages/VideoPage/components/CoverPicker.tsx +32 -0
- package/studio/web/pages/VideoPage/components/EditorStage.tsx +53 -0
- package/studio/web/pages/VideoPage/components/LineTakes.tsx +160 -0
- package/studio/web/pages/VideoPage/components/LineTextEditor.tsx +124 -0
- package/studio/web/pages/VideoPage/components/LineVoicePicker.tsx +37 -0
- package/studio/web/pages/VideoPage/components/MediaImage.tsx +25 -0
- package/studio/web/pages/VideoPage/components/MotionScriptStage.tsx +39 -0
- package/studio/web/pages/VideoPage/components/NarrationStage.tsx +70 -0
- package/studio/web/pages/VideoPage/components/PublishForm.tsx +387 -0
- package/studio/web/pages/VideoPage/components/PublishStage.tsx +76 -0
- package/studio/web/pages/VideoPage/components/PublishText.tsx +149 -0
- package/studio/web/pages/VideoPage/components/ScenesApprovalPanel.tsx +278 -0
- package/studio/web/pages/VideoPage/components/ScheduledInfo.tsx +72 -0
- package/studio/web/pages/VideoPage/components/ScriptApprovalPanel.tsx +189 -0
- package/studio/web/pages/VideoPage/components/ScriptStage.tsx +17 -0
- package/studio/web/pages/VideoPage/components/SequencePlayer.tsx +146 -0
- package/studio/web/pages/VideoPage/components/SoundTimeline/AddSound.tsx +117 -0
- package/studio/web/pages/VideoPage/components/SoundTimeline/AreaSelection.tsx +74 -0
- package/studio/web/pages/VideoPage/components/SoundTimeline/BedPanel.tsx +94 -0
- package/studio/web/pages/VideoPage/components/SoundTimeline/BedRow.tsx +45 -0
- package/studio/web/pages/VideoPage/components/SoundTimeline/CaptionOverlay.tsx +44 -0
- package/studio/web/pages/VideoPage/components/SoundTimeline/CaptionSaveBar.tsx +32 -0
- package/studio/web/pages/VideoPage/components/SoundTimeline/CaptionsPanel.tsx +22 -0
- package/studio/web/pages/VideoPage/components/SoundTimeline/ClipBlock.tsx +111 -0
- package/studio/web/pages/VideoPage/components/SoundTimeline/ClipPanel.tsx +76 -0
- package/studio/web/pages/VideoPage/components/SoundTimeline/CutsPanel.tsx +44 -0
- package/studio/web/pages/VideoPage/components/SoundTimeline/EditorTabBar.tsx +73 -0
- package/studio/web/pages/VideoPage/components/SoundTimeline/EffectChoice.tsx +46 -0
- package/studio/web/pages/VideoPage/components/SoundTimeline/EffectItem.tsx +89 -0
- package/studio/web/pages/VideoPage/components/SoundTimeline/EffectPanel.tsx +232 -0
- package/studio/web/pages/VideoPage/components/SoundTimeline/EffectsRow.tsx +21 -0
- package/studio/web/pages/VideoPage/components/SoundTimeline/Lane.tsx +39 -0
- package/studio/web/pages/VideoPage/components/SoundTimeline/MarkerRow.tsx +40 -0
- package/studio/web/pages/VideoPage/components/SoundTimeline/MuteButton.tsx +36 -0
- package/studio/web/pages/VideoPage/components/SoundTimeline/NumberInput.tsx +37 -0
- package/studio/web/pages/VideoPage/components/SoundTimeline/PlanWarnings.tsx +19 -0
- package/studio/web/pages/VideoPage/components/SoundTimeline/PlayerResizeHandle.tsx +50 -0
- package/studio/web/pages/VideoPage/components/SoundTimeline/PreviewControls.tsx +51 -0
- package/studio/web/pages/VideoPage/components/SoundTimeline/SceneRow.tsx +54 -0
- package/studio/web/pages/VideoPage/components/SoundTimeline/SettingsPanel.tsx +65 -0
- package/studio/web/pages/VideoPage/components/SoundTimeline/SoundLibrary.tsx +99 -0
- package/studio/web/pages/VideoPage/components/SoundTimeline/SoundPreview.tsx +82 -0
- package/studio/web/pages/VideoPage/components/SoundTimeline/SpeedPanel.tsx +128 -0
- package/studio/web/pages/VideoPage/components/SoundTimeline/TimelineDock.tsx +56 -0
- package/studio/web/pages/VideoPage/components/SoundTimeline/TimelineFooter.tsx +26 -0
- package/studio/web/pages/VideoPage/components/SoundTimeline/TimelineSkeleton.tsx +21 -0
- package/studio/web/pages/VideoPage/components/SoundTimeline/TimelineToolbar.tsx +160 -0
- package/studio/web/pages/VideoPage/components/SoundTimeline/TimelineTracks.tsx +121 -0
- package/studio/web/pages/VideoPage/components/SoundTimeline/TrackSidebar.tsx +30 -0
- package/studio/web/pages/VideoPage/components/SoundTimeline/VoiceRow.tsx +53 -0
- package/studio/web/pages/VideoPage/components/SoundTimeline/Waveform.tsx +97 -0
- package/studio/web/pages/VideoPage/components/SoundTimeline/clipActions.ts +50 -0
- package/studio/web/pages/VideoPage/components/SoundTimeline/effectActions.ts +161 -0
- package/studio/web/pages/VideoPage/components/SoundTimeline/index.tsx +161 -0
- package/studio/web/pages/VideoPage/components/SoundTimeline/timelineGeometry.ts +45 -0
- package/studio/web/pages/VideoPage/components/SoundTimeline/types.ts +43 -0
- package/studio/web/pages/VideoPage/components/SoundTimeline/useAutosave.ts +107 -0
- package/studio/web/pages/VideoPage/components/SoundTimeline/useEditorShortcuts.ts +168 -0
- package/studio/web/pages/VideoPage/components/SoundTimeline/useFitHeight.ts +40 -0
- package/studio/web/pages/VideoPage/components/SoundTimeline/useFollowPlayhead.ts +21 -0
- package/studio/web/pages/VideoPage/components/SoundTimeline/useListenMix.ts +43 -0
- package/studio/web/pages/VideoPage/components/SoundTimeline/usePlayback.ts +231 -0
- package/studio/web/pages/VideoPage/components/SoundTimeline/usePlayerWidth.ts +27 -0
- package/studio/web/pages/VideoPage/components/SoundTimeline/useSoundData.ts +124 -0
- package/studio/web/pages/VideoPage/components/SoundTimeline/useSoundTimeline.ts +511 -0
- package/studio/web/pages/VideoPage/components/StageDrawer/CompareVersions.tsx +111 -0
- package/studio/web/pages/VideoPage/components/StageDrawer/VersionCard.tsx +81 -0
- package/studio/web/pages/VideoPage/components/StageDrawer/VersionMedia.tsx +24 -0
- package/studio/web/pages/VideoPage/components/StageDrawer/VersionText.tsx +15 -0
- package/studio/web/pages/VideoPage/components/StageDrawer/index.tsx +109 -0
- package/studio/web/pages/VideoPage/components/StageProcess.tsx +126 -0
- package/studio/web/pages/VideoPage/components/StageResult.tsx +69 -0
- package/studio/web/pages/VideoPage/components/SwapReference.tsx +92 -0
- package/studio/web/pages/VideoPage/components/ThumbnailPanel.tsx +252 -0
- package/studio/web/pages/VideoPage/components/TimingPanel.tsx +89 -0
- package/studio/web/pages/VideoPage/components/VideoPlayer.tsx +18 -0
- package/studio/web/pages/VideoPage/components/VideoSkeleton.tsx +50 -0
- package/studio/web/pages/VideoPage/components/VideoVoiceInfo.tsx +33 -0
- package/studio/web/pages/VideoPage/components/VideoVoicePicker.tsx +165 -0
- package/studio/web/pages/VideoPage/components/VoicePauseBanner.tsx +56 -0
- package/studio/web/pages/VideoPage/components/WithLogo.tsx +10 -0
- package/studio/web/pages/VideoPage/index.tsx +295 -0
- package/studio/web/pages/VideosPage/components/PublishedOn.tsx +85 -0
- package/studio/web/pages/VideosPage/components/ServicesPanel.tsx +81 -0
- package/studio/web/pages/VideosPage/components/SessionsPanel.tsx +62 -0
- package/studio/web/pages/VideosPage/components/VideoListSkeleton.tsx +25 -0
- package/studio/web/pages/VideosPage/components/VideoRow.tsx +122 -0
- package/studio/web/pages/VideosPage/index.tsx +134 -0
- package/studio/web/pages/VoicePage/index.tsx +28 -0
- package/studio/web/router.tsx +201 -0
- package/studio/web/styles/base/elements.css +55 -0
- package/studio/web/styles/base/utilities.css +25 -0
- package/studio/web/styles/components/badge.css +33 -0
- package/studio/web/styles/components/claude-running.css +27 -0
- package/studio/web/styles/components/claude-session.css +212 -0
- package/studio/web/styles/components/claude-walking.css +92 -0
- package/studio/web/styles/components/clone-voice-save.css +136 -0
- package/studio/web/styles/components/clone-voice.css +379 -0
- package/studio/web/styles/components/controls.css +70 -0
- package/studio/web/styles/components/data-and-image.css +54 -0
- package/studio/web/styles/components/forms.css +132 -0
- package/studio/web/styles/components/human-banner.css +99 -0
- package/studio/web/styles/components/lightbox.css +50 -0
- package/studio/web/styles/components/markdown.css +68 -0
- package/studio/web/styles/components/modal.css +70 -0
- package/studio/web/styles/components/omni-cards.css +105 -0
- package/studio/web/styles/components/omni.css +859 -0
- package/studio/web/styles/components/omnichat.css +538 -0
- package/studio/web/styles/components/panel.css +115 -0
- package/studio/web/styles/components/platform-card.css +140 -0
- package/studio/web/styles/components/provider-card.css +132 -0
- package/studio/web/styles/components/queue.css +185 -0
- package/studio/web/styles/components/reference-actions.css +133 -0
- package/studio/web/styles/components/reference-picker.css +146 -0
- package/studio/web/styles/components/results-drawer.css +224 -0
- package/studio/web/styles/components/segmented.css +115 -0
- package/studio/web/styles/components/side-panel-tab.css +23 -0
- package/studio/web/styles/components/status-signals.css +66 -0
- package/studio/web/styles/components/stepper.css +208 -0
- package/studio/web/styles/components/table.css +19 -0
- package/studio/web/styles/components/tabs.css +24 -0
- package/studio/web/styles/components/ui/accordion.css +71 -0
- package/studio/web/styles/components/ui/chart.css +162 -0
- package/studio/web/styles/components/ui/checkbox.css +37 -0
- package/studio/web/styles/components/ui/date-picker.css +153 -0
- package/studio/web/styles/components/ui/select.css +110 -0
- package/studio/web/styles/components/ui/skeleton.css +25 -0
- package/studio/web/styles/components/ui/slider.css +81 -0
- package/studio/web/styles/components/ui/switch.css +41 -0
- package/studio/web/styles/components/ui/tooltip.css +24 -0
- package/studio/web/styles/components/voice-carousel.css +255 -0
- package/studio/web/styles/components/voice-editor.css +179 -0
- package/studio/web/styles/components/voice-grid.css +56 -0
- package/studio/web/styles/components/voice-profiles.css +192 -0
- package/studio/web/styles/design-system.md +136 -0
- package/studio/web/styles/index.css +116 -0
- package/studio/web/styles/layout/page.css +41 -0
- package/studio/web/styles/layout/sidebar-groups.css +39 -0
- package/studio/web/styles/layout/sidebar.css +111 -0
- package/studio/web/styles/pages/agent.css +26 -0
- package/studio/web/styles/pages/analytics/dashboard.css +614 -0
- package/studio/web/styles/pages/analytics/insights.css +48 -0
- package/studio/web/styles/pages/getting-started.css +166 -0
- package/studio/web/styles/pages/new-video/form.css +32 -0
- package/studio/web/styles/pages/not-found.css +59 -0
- package/studio/web/styles/pages/references.css +204 -0
- package/studio/web/styles/pages/schedule/calendar.css +541 -0
- package/studio/web/styles/pages/schedule/chip-states.css +60 -0
- package/studio/web/styles/pages/settings/models.css +72 -0
- package/studio/web/styles/pages/settings/shell.css +361 -0
- package/studio/web/styles/pages/settings/usage.css +63 -0
- package/studio/web/styles/pages/studio-offline.css +105 -0
- package/studio/web/styles/pages/video/caption-controls.css +194 -0
- package/studio/web/styles/pages/video/captions-grid.css +70 -0
- package/studio/web/styles/pages/video/captions.css +79 -0
- package/studio/web/styles/pages/video/editor-chat.css +103 -0
- package/studio/web/styles/pages/video/export.css +50 -0
- package/studio/web/styles/pages/video/frames.css +29 -0
- package/studio/web/styles/pages/video/fullscreen-stage.css +400 -0
- package/studio/web/styles/pages/video/instagram-carousel.css +309 -0
- package/studio/web/styles/pages/video/layout.css +285 -0
- package/studio/web/styles/pages/video/narration.css +138 -0
- package/studio/web/styles/pages/video/preview-overview.css +11 -0
- package/studio/web/styles/pages/video/processing-notice.css +34 -0
- package/studio/web/styles/pages/video/publish-when.css +76 -0
- package/studio/web/styles/pages/video/publish.css +97 -0
- package/studio/web/styles/pages/video/result.css +96 -0
- package/studio/web/styles/pages/video/scenes.css +167 -0
- package/studio/web/styles/pages/video/sound-timeline.css +672 -0
- package/studio/web/styles/pages/video/sound-toolbar.css +149 -0
- package/studio/web/styles/pages/video/take-player.css +100 -0
- package/studio/web/styles/pages/video/thumbnail.css +157 -0
- package/studio/web/styles/pages/video/timeline.css +117 -0
- package/studio/web/styles/pages/video/voice-picker.css +14 -0
- package/studio/web/styles/pages/video-list/active-row.css +27 -0
- package/studio/web/styles/pages/video-list/card-link.css +49 -0
- package/studio/web/styles/pages/video-list/list.css +86 -0
- package/studio/web/styles/theme/identity/details.css +204 -0
- package/studio/web/styles/theme/identity/shell.css +249 -0
- package/studio/web/styles/theme/identity/surfaces.css +183 -0
- package/studio/web/styles/theme/identity/titles-and-buttons.css +120 -0
- package/studio/web/styles/theme/no-borders/blocks.css +208 -0
- package/studio/web/styles/theme/no-borders/controls.css +256 -0
- package/studio/web/styles/tokens.css +245 -0
- package/studio/web/types/api.ts +2 -0
- package/studio/web/types/assets.d.ts +9 -0
- package/templates/brief.md +27 -0
- package/templates/captions.html +204 -0
- package/templates/carousel-composition.md +47 -0
- package/templates/carousel-script.md +60 -0
- package/templates/components/voiceavatar/README.md +87 -0
- package/templates/components/voiceavatar/demo.html +50 -0
- package/templates/components/voiceavatar/voiceavatar.js +415 -0
- package/templates/motion.md +80 -0
- package/templates/playbook.md +43 -0
- package/templates/reference.md +123 -0
- package/templates/scene.html +112 -0
- package/templates/scenes.example.json +96 -0
- package/templates/script.md +63 -0
- package/templates/slide.html +71 -0
- package/templates/wireframe.html +91 -0
- package/tools/analysis.mjs +300 -0
- package/tools/approval.mjs +442 -0
- package/tools/assemble.mjs +942 -0
- package/tools/build-motion.mjs +437 -0
- package/tools/captions.mjs +246 -0
- package/tools/carousel.mjs +558 -0
- package/tools/compose.mjs +307 -0
- package/tools/cuts.mjs +74 -0
- package/tools/editor.mjs +419 -0
- package/tools/epidemic-capture.js +54 -0
- package/tools/fanout.mjs +69 -0
- package/tools/history.mjs +70 -0
- package/tools/history.py +61 -0
- package/tools/job-queue.mjs +180 -0
- package/tools/job_queue.py +430 -0
- package/tools/lib/agents.mjs +412 -0
- package/tools/lib/channel.mjs +399 -0
- package/tools/lib/claude-settings.mjs +67 -0
- package/tools/lib/cli.mjs +9 -0
- package/tools/lib/fanout.mjs +234 -0
- package/tools/lib/http.mjs +94 -0
- package/tools/lib/index.js +14 -0
- package/tools/lib/mcp-server.mjs +69 -0
- package/tools/lib/metrics/metrics.mjs +204 -0
- package/tools/lib/narration.mjs +15 -0
- package/tools/lib/oauth-client.mjs +295 -0
- package/tools/lib/omnichat/config.mjs +139 -0
- package/tools/lib/omnichat/errors.mjs +42 -0
- package/tools/lib/omnichat/extract.mjs +82 -0
- package/tools/lib/omnichat/gate.mjs +151 -0
- package/tools/lib/omnichat/manual.mjs +166 -0
- package/tools/lib/omnichat/match.mjs +78 -0
- package/tools/lib/omnichat/pass.mjs +393 -0
- package/tools/lib/omnichat/record.mjs +208 -0
- package/tools/lib/omnichat/text.mjs +315 -0
- package/tools/lib/omnichat/webhook.mjs +166 -0
- package/tools/lib/pairing.mjs +99 -0
- package/tools/lib/paths.mjs +41 -0
- package/tools/lib/platforms/instagram.mjs +469 -0
- package/tools/lib/platforms/tiktok.mjs +343 -0
- package/tools/lib/platforms/youtube.mjs +228 -0
- package/tools/lib/publish/publish.mjs +244 -0
- package/tools/lib/schedule/schedule.mjs +191 -0
- package/tools/lib/service.mjs +134 -0
- package/tools/lib/skill-client.mjs +126 -0
- package/tools/lib/skills-release.mjs +196 -0
- package/tools/lib/testing.mjs +52 -0
- package/tools/lib/up.mjs +187 -0
- package/tools/metrics.mjs +415 -0
- package/tools/models.mjs +134 -0
- package/tools/narrate.mjs +257 -0
- package/tools/omni.mjs +298 -0
- package/tools/omni_paths.py +12 -0
- package/tools/omnichat.mjs +1577 -0
- package/tools/omnivideo.py +1122 -0
- package/tools/package.mjs +130 -0
- package/tools/platforms.mjs +236 -0
- package/tools/premiere.mjs +533 -0
- package/tools/publish.mjs +1496 -0
- package/tools/reference.mjs +1408 -0
- package/tools/render.mjs +426 -0
- package/tools/runner.mjs +156 -0
- package/tools/setup.mjs +466 -0
- package/tools/skills-release.mjs +93 -0
- package/tools/sound.mjs +1323 -0
- package/tools/soundtrack.mjs +217 -0
- package/tools/thumbnail.mjs +271 -0
- package/tools/timing.mjs +108 -0
- package/tools/usage.py +628 -0
- package/tools/voice-envelope.mjs +185 -0
- package/tools/voice.py +448 -0
- package/tools/voice_sample.py +75 -0
- package/tools/wireframe.mjs +230 -0
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
# VoiceStudio
|
|
2
|
+
|
|
3
|
+
Repository and environment in a folder of yours (`voicestudio.repo` in the
|
|
4
|
+
config; a clone of `debpalash/VoiceStudio` v0.5.7, called `<repo>` below).
|
|
5
|
+
Local FastAPI backend at `http://127.0.0.1:3900`, OmniVoice model on the Mac's
|
|
6
|
+
GPU (MPS). No API key: everything runs locally.
|
|
7
|
+
|
|
8
|
+
## Start
|
|
9
|
+
|
|
10
|
+
- App: `cd <repo> && bun run dev` (opens Electron, which starts the backend).
|
|
11
|
+
- Backend only: `bun run dev:api`.
|
|
12
|
+
- Check: `curl -s http://127.0.0.1:3900/health`.
|
|
13
|
+
|
|
14
|
+
## Voice
|
|
15
|
+
|
|
16
|
+
**Always the profile named "Pedro".** The id changes every time the clone is
|
|
17
|
+
redone, so the config keeps the name (`voicestudio.profile`) and
|
|
18
|
+
`omnivideo.py voice` looks up the id in `GET /profiles` on every run. Zero or
|
|
19
|
+
more than one profile with that name is an error.
|
|
20
|
+
|
|
21
|
+
> **Trap:** `POST /generate` with a `profile_id` that doesn't exist **does not
|
|
22
|
+
> error**. It generates with a generic voice, no clone, and faster. That is how
|
|
23
|
+
> a test came out with the wrong voice without any warning. That's why the id
|
|
24
|
+
> is never hardcoded.
|
|
25
|
+
|
|
26
|
+
Current profile (2026-10-12): `c75067ce`, 66 s reference, language
|
|
27
|
+
Portuguese, no saved transcription. OmniVoice uses at most 20 s of the
|
|
28
|
+
reference: with a longer clip, it picks the 15 s excerpt with the most speech
|
|
29
|
+
by itself.
|
|
30
|
+
|
|
31
|
+
## Generate
|
|
32
|
+
|
|
33
|
+
`POST /generate` (multipart), response `audio/wav` 24 kHz mono:
|
|
34
|
+
|
|
35
|
+
| field | value in omnivideo |
|
|
36
|
+
|---|---|
|
|
37
|
+
| `text` | the line |
|
|
38
|
+
| `profile_id` | resolved by name |
|
|
39
|
+
| `language` | `Portuguese` |
|
|
40
|
+
| `num_step` | 16 (8 = quick draft, 32 = quality) |
|
|
41
|
+
| `speed` | 1.0 |
|
|
42
|
+
| `instruct` | optional, per line |
|
|
43
|
+
|
|
44
|
+
Every generation goes through the default mastering (`broadcast` preset),
|
|
45
|
+
gets an inaudible watermark (AudioSeal) and goes into the app's history.
|
|
46
|
+
|
|
47
|
+
## Measured performance (M4, 16 GB)
|
|
48
|
+
|
|
49
|
+
| situation | cost |
|
|
50
|
+
|---|---|
|
|
51
|
+
| "Pedro" profile, model already loaded | ~20 s fixed per line + 4 to 5 s per second of audio |
|
|
52
|
+
| first line after being idle | +10 s (loading the model; it unloads after 5 min idle) |
|
|
53
|
+
| memory full (swap ≥ 90 %) | up to 9 s per second of audio; long lines blow the limit |
|
|
54
|
+
|
|
55
|
+
The limit is **305 s of compute per generation**. If it blows: the abandoned
|
|
56
|
+
job keeps running until it finishes, and retrying right away competes with
|
|
57
|
+
it. Wait, split the line, and warn the user about memory
|
|
58
|
+
(`sysctl vm.swapusage`).
|
|
59
|
+
|
|
60
|
+
Pace of Pedro's voice: 2.3 to 3.4 words per second; the config uses 3.0 to
|
|
61
|
+
estimate.
|
|
62
|
+
|
|
63
|
+
## Pronunciation
|
|
64
|
+
|
|
65
|
+
A word coming out wrong goes into the pronunciation dictionary, which applies
|
|
66
|
+
to every generation:
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
curl -s -X POST http://127.0.0.1:3900/pronunciation -H 'content-type: application/json' \
|
|
70
|
+
-d '{"term": "MCP", "replacement": "éme cê pê", "language": "*"}'
|
|
71
|
+
curl -s http://127.0.0.1:3900/pronunciation # list
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
You can also fix a single occurrence, in the text: `[[éme cê pê]]`. But in
|
|
75
|
+
omnivideo the line's text is also the caption, so prefer the dictionary.
|
|
76
|
+
|
|
77
|
+
## MCP
|
|
78
|
+
|
|
79
|
+
VoiceStudio also exposes MCP at `http://localhost:3900/mcp/` (registered in
|
|
80
|
+
Claude Code as `voicestudio`). For omnivideo use the API: the MCP returns the
|
|
81
|
+
audio in base64 inside the conversation, which is very heavy.
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
# Writing for the voice
|
|
2
|
+
|
|
3
|
+
The line goes straight to the cloned voice (Fish Audio by default). The voice
|
|
4
|
+
reads punctuation as intonation: each mark becomes a pause, a rise or a break.
|
|
5
|
+
Text that looks good written can come out choppy spoken. The rule is to write
|
|
6
|
+
the way Pedro would speak in a conversation, not the way he would write a post.
|
|
7
|
+
|
|
8
|
+
Applies to `script.md` (stage 1) and, above all, to the lines in
|
|
9
|
+
`scenes.json` (stage 4). `omnivideo.py status` warns when a
|
|
10
|
+
line breaks the objective rules below.
|
|
11
|
+
|
|
12
|
+
The narration is Portuguese, so the examples below stay in Portuguese.
|
|
13
|
+
|
|
14
|
+
## Punctuation
|
|
15
|
+
|
|
16
|
+
- **The period is the only safe mark.** One sentence, one idea, period.
|
|
17
|
+
- **No colons.** The voice stops and changes tone mid-sentence. Whatever would
|
|
18
|
+
come after the colon becomes another sentence.
|
|
19
|
+
- **At most one comma**, and only where people naturally breathe ("No
|
|
20
|
+
Upwork, …"). A comma before a colon is the worst case: two breaks in a row
|
|
21
|
+
in the middle of one idea.
|
|
22
|
+
- **No appositives or parenthetical clauses** ("que pede, por escrito, …").
|
|
23
|
+
The voice breaks the intonation at the parenthetical and never recovers.
|
|
24
|
+
- **No semicolons, dashes, parentheses, slashes or ellipses.**
|
|
25
|
+
- **A list becomes a sentence.** "história curta, referência visual e som"
|
|
26
|
+
after a colon sounds like reading bullet points. Write it with a verb and
|
|
27
|
+
articles: "Você precisa de uma história curta, uma referência visual e um som."
|
|
28
|
+
|
|
29
|
+
## Order and rhythm
|
|
30
|
+
|
|
31
|
+
- **Direct order:** subject, verb, complement. Don't open with a long
|
|
32
|
+
circumstance.
|
|
33
|
+
- **One idea per sentence.** Two price ranges in one sentence become two
|
|
34
|
+
sentences (and two lines, which the motion can separate).
|
|
35
|
+
- **Short sentences.** Up to ~15 words sounds natural; 22 is the stage's ceiling.
|
|
36
|
+
- **Colloquial:** "pra", "a gente", "tá", contractions the way people speak.
|
|
37
|
+
|
|
38
|
+
## Words
|
|
39
|
+
|
|
40
|
+
- **Numbers as spoken:** "500 dólares", "2 mil", "15 segundos". Ranges with
|
|
41
|
+
"de … a …", never a hyphen or dash.
|
|
42
|
+
- **Don't end the sentence on a bare number.** "chega a 2 mil." left the voice
|
|
43
|
+
without a clear ending, and it tacked on an extra sound. Complete it with
|
|
44
|
+
the unit: "chega a 2 mil dólares."
|
|
45
|
+
- **Version numbers, mixed acronyms and hard foreign words** ("Opus 5.5",
|
|
46
|
+
"SaaS") have no pronunciation dictionary in Fish. Leave them on screen and
|
|
47
|
+
say them in Portuguese ("o Opus", "demo de produto"). The motion shows the
|
|
48
|
+
exact term.
|
|
49
|
+
- **No symbols:** "%" becomes "por cento", "x" becomes "vezes", "→" doesn't exist.
|
|
50
|
+
- AI terms Pedro says in English (prompt, agent, token) stay.
|
|
51
|
+
|
|
52
|
+
## Real example (2026-10-12)
|
|
53
|
+
|
|
54
|
+
| written | what the voice did | rewritten |
|
|
55
|
+
|---|---|---|
|
|
56
|
+
| E tem vaga que pede, por escrito: domínio do Opus 5.5. | two breaks in a row (comma and colon) and "5.5" read as a number | E já tem vaga pedindo quem domine o Opus. |
|
|
57
|
+
| No Upwork, vai de 100 dólares por um logo animado até 2 mil por uma demo de SaaS. | long sentence, two ranges | Um logo animado no Upwork sai por 100 dólares. / Uma demo de produto chega a 2 mil dólares. |
|
|
58
|
+
| Começa por demo de SaaS: história curta, referência visual e som. | colon and a bullet-point list | O melhor jeito de começar é a demo de produto. / Você precisa de uma história curta, uma referência visual e um som. |
|
|
59
|
+
| Tem gente vendendo animação a 129 dólares, feita em menos de uma hora. | parenthetical at the end | Tem gente cobrando 129 dólares por uma animação que leva menos de uma hora. |
|
|
60
|
+
| Uma demo de produto chega a 2 mil. | ended on a bare number; the voice tacked a sound onto the end | Uma demo de produto chega a 2 mil dólares. |
|
|
61
|
+
|
|
62
|
+
## How to check
|
|
63
|
+
|
|
64
|
+
Read the line out loud, for real, once. If you stopped or changed tone
|
|
65
|
+
anywhere other than the period, rewrite it. After the voice, the voice stage
|
|
66
|
+
marks `← ritmo estranho` (odd rhythm) when a line comes out too fast or too
|
|
67
|
+
slow: it is almost always a hard sentence, not a voice error.
|
package/requirements.txt
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/* The studio's HTTP API (the local runner API): every /api route plus the file routes (/media,
|
|
2
|
+
/ref, /kit, /sound-draft, /docs). Bun keeps serving the HTML bundle (studio.ts) and hands these
|
|
3
|
+
paths here. `AppType` types the web client (web/lib/api.ts, hono/client).
|
|
4
|
+
|
|
5
|
+
Routers are domain modules under routes/; the cloud candidates (publish, schedule, omnichat,
|
|
6
|
+
analytics) only reach their own services, so they can move to an edge function as they are. */
|
|
7
|
+
|
|
8
|
+
import { router } from "./lib/hono";
|
|
9
|
+
import { notFoundResponse, onError } from "./lib/errors";
|
|
10
|
+
import { localOnly } from "./middleware/local-only";
|
|
11
|
+
import { jsonCharset } from "./middleware/json-charset";
|
|
12
|
+
import { admin } from "./routes/admin";
|
|
13
|
+
import { agent } from "./routes/agent";
|
|
14
|
+
import { agentCascade } from "./routes/agent-cascade";
|
|
15
|
+
import { agentHistory } from "./routes/agent-history";
|
|
16
|
+
import { analytics } from "./routes/analytics";
|
|
17
|
+
import { approval } from "./routes/approval";
|
|
18
|
+
import { archive } from "./routes/archive";
|
|
19
|
+
import { captions } from "./routes/captions";
|
|
20
|
+
import { carousel } from "./routes/carousel";
|
|
21
|
+
import { clone } from "./routes/clone";
|
|
22
|
+
import { create, UPLOAD_PATH } from "./routes/create";
|
|
23
|
+
import { events } from "./routes/events";
|
|
24
|
+
import { exportRoutes } from "./routes/export";
|
|
25
|
+
import { media } from "./routes/media";
|
|
26
|
+
import { omnichat } from "./routes/omnichat";
|
|
27
|
+
import { onboarding } from "./routes/onboarding";
|
|
28
|
+
import { publish } from "./routes/publish";
|
|
29
|
+
import { queue } from "./routes/queue";
|
|
30
|
+
import { references } from "./routes/references";
|
|
31
|
+
import { runner } from "./routes/runner";
|
|
32
|
+
import { schedule } from "./routes/schedule";
|
|
33
|
+
import { services } from "./routes/services";
|
|
34
|
+
import { sessions } from "./routes/sessions";
|
|
35
|
+
import { settings } from "./routes/settings";
|
|
36
|
+
import { sound } from "./routes/sound";
|
|
37
|
+
import { thumbnail } from "./routes/thumbnail";
|
|
38
|
+
import { usage } from "./routes/usage";
|
|
39
|
+
import { videos } from "./routes/videos";
|
|
40
|
+
import { voices } from "./routes/voices";
|
|
41
|
+
|
|
42
|
+
/* the path prefixes studio.ts hands to this app */
|
|
43
|
+
export const API_PREFIXES = ["/api", "/media", "/ref", "/kit", "/sound-draft", "/docs"] as const;
|
|
44
|
+
|
|
45
|
+
export type AppOptions = { port: number };
|
|
46
|
+
|
|
47
|
+
export function createApp({ port }: AppOptions) {
|
|
48
|
+
const api = router()
|
|
49
|
+
.route("/", create)
|
|
50
|
+
.route("/", videos)
|
|
51
|
+
.route("/", captions)
|
|
52
|
+
.route("/", sound)
|
|
53
|
+
.route("/", exportRoutes)
|
|
54
|
+
.route("/", voices)
|
|
55
|
+
.route("/", approval)
|
|
56
|
+
.route("/", archive)
|
|
57
|
+
.route("/", thumbnail)
|
|
58
|
+
.route("/", carousel)
|
|
59
|
+
.route("/", publish)
|
|
60
|
+
.route("/", schedule)
|
|
61
|
+
.route("/", omnichat)
|
|
62
|
+
.route("/", sessions)
|
|
63
|
+
.route("/", references)
|
|
64
|
+
.route("/", queue)
|
|
65
|
+
.route("/", runner)
|
|
66
|
+
.route("/", services)
|
|
67
|
+
.route("/", usage)
|
|
68
|
+
.route("/", analytics)
|
|
69
|
+
.route("/", settings)
|
|
70
|
+
.route("/", clone)
|
|
71
|
+
.route("/", onboarding)
|
|
72
|
+
.route("/", admin)
|
|
73
|
+
.route("/", agent)
|
|
74
|
+
.route("/", agentCascade)
|
|
75
|
+
.route("/", agentHistory)
|
|
76
|
+
.route("/", events);
|
|
77
|
+
const app = router()
|
|
78
|
+
.use(localOnly({ port, uploadPaths: [UPLOAD_PATH] }))
|
|
79
|
+
.use(jsonCharset)
|
|
80
|
+
.route("/api", api)
|
|
81
|
+
.route("/", media);
|
|
82
|
+
app.onError(onError);
|
|
83
|
+
app.notFound(() => notFoundResponse());
|
|
84
|
+
return app;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export type AppType = ReturnType<typeof createApp>;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/* One error format. HttpError carries a status; 404 stays the plain "não encontrado" text the
|
|
2
|
+
studio always answered, everything else is the { ok: false, error } envelope. */
|
|
3
|
+
|
|
4
|
+
import type { Context } from "hono";
|
|
5
|
+
import { HTTPException } from "hono/http-exception";
|
|
6
|
+
|
|
7
|
+
export class HttpError extends Error {
|
|
8
|
+
constructor(
|
|
9
|
+
readonly status: 400 | 403 | 404 | 415 | 500,
|
|
10
|
+
message: string,
|
|
11
|
+
) {
|
|
12
|
+
super(message);
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export const NOT_FOUND_TEXT = "não encontrado";
|
|
17
|
+
export const notFoundResponse = () => new Response(NOT_FOUND_TEXT, { status: 404 });
|
|
18
|
+
|
|
19
|
+
/* throw from a handler: answered by onError as the plain 404 */
|
|
20
|
+
export const notFound = () => new HttpError(404, NOT_FOUND_TEXT);
|
|
21
|
+
|
|
22
|
+
export function onError(e: Error, c: Context): Response {
|
|
23
|
+
if (e instanceof HttpError) {
|
|
24
|
+
if (e.status === 404) return notFoundResponse();
|
|
25
|
+
return c.json({ ok: false, error: e.message }, e.status);
|
|
26
|
+
}
|
|
27
|
+
/* the validator's malformed JSON */
|
|
28
|
+
if (e instanceof HTTPException && e.status === 400) {
|
|
29
|
+
return c.json({ ok: false, error: "corpo da requisição: JSON inválido" }, 400);
|
|
30
|
+
}
|
|
31
|
+
console.error(e);
|
|
32
|
+
return c.json({ ok: false, error: e.message || String(e) }, 500);
|
|
33
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/* Files served as they are (media, reference files, docs, sound drafts, kit). Bun answers Range
|
|
2
|
+
by itself for Bun.file (206 with Content-Range): it is what video and audio players ask for
|
|
3
|
+
to seek into the middle of the file. */
|
|
4
|
+
|
|
5
|
+
import { extname } from "node:path";
|
|
6
|
+
import { notFoundResponse } from "./errors";
|
|
7
|
+
|
|
8
|
+
const TEXT_CONTENT_TYPES: Record<string, string> = {
|
|
9
|
+
".srt": "text/plain; charset=utf-8",
|
|
10
|
+
".md": "text/plain; charset=utf-8",
|
|
11
|
+
".txt": "text/plain; charset=utf-8",
|
|
12
|
+
".json": "application/json; charset=utf-8",
|
|
13
|
+
".css": "text/plain; charset=utf-8",
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
/* the path segments after the first `skip` ones, decoded: /media/<slug>/<a>/<b> → [slug, a, b] */
|
|
17
|
+
export const pathSegments = (url: string, skip = 2) =>
|
|
18
|
+
new URL(url).pathname
|
|
19
|
+
.split("/")
|
|
20
|
+
.slice(skip)
|
|
21
|
+
.map(p => decodeURIComponent(p));
|
|
22
|
+
|
|
23
|
+
/* `?v=` (the file's mtime) makes the URL immutable */
|
|
24
|
+
export async function serveFile(abs: string | null, url: string): Promise<Response> {
|
|
25
|
+
if (!abs) return notFoundResponse();
|
|
26
|
+
const file = Bun.file(abs);
|
|
27
|
+
if (!(await file.exists())) return notFoundResponse();
|
|
28
|
+
const headers: Record<string, string> = {
|
|
29
|
+
"cache-control": new URL(url).searchParams.has("v") ? "private, max-age=31536000, immutable" : "no-cache",
|
|
30
|
+
"x-content-type-options": "nosniff",
|
|
31
|
+
};
|
|
32
|
+
const type = TEXT_CONTENT_TYPES[extname(abs).toLowerCase()];
|
|
33
|
+
if (type) headers["content-type"] = type;
|
|
34
|
+
return new Response(file, { headers });
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/* a stored WAV (voice samples, original recordings): 404 when missing or empty */
|
|
38
|
+
export function serveWav(abs: string | null): Response {
|
|
39
|
+
const f = abs ? Bun.file(abs) : null;
|
|
40
|
+
return f?.size ? new Response(f, { headers: { "content-type": "audio/wav" } }) : notFoundResponse();
|
|
41
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/* The Hono env every router shares: Bun's server (for server.timeout on long requests and
|
|
2
|
+
the SSE stream) arrives as c.env.server; tests pass none. */
|
|
3
|
+
|
|
4
|
+
import { Hono } from "hono";
|
|
5
|
+
import type { Server } from "bun";
|
|
6
|
+
|
|
7
|
+
export type AppEnv = { Bindings: { server?: Server<unknown> } };
|
|
8
|
+
|
|
9
|
+
export const router = () => new Hono<AppEnv>();
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/* Envelope helpers. A service result ({ ok, output?, error?, … }) answers 200 when ok and 400
|
|
2
|
+
when not, as the studio always did. */
|
|
3
|
+
|
|
4
|
+
import type { Context } from "hono";
|
|
5
|
+
|
|
6
|
+
export const result = <T extends { ok: boolean }>(c: Context, r: T) => c.json(r, r.ok ? 200 : 400);
|
|
7
|
+
|
|
8
|
+
/* a failed result with this message (400) */
|
|
9
|
+
export const fail = (c: Context, error: string) => c.json({ ok: false as const, error }, 400);
|
|
10
|
+
|
|
11
|
+
/* A service value under the response type the UI reads (shared/types/api.ts). Services build these
|
|
12
|
+
from the video's files and the tools' JSON output, so the shared type is the contract between
|
|
13
|
+
the two sides rather than something TS can prove from the service code. */
|
|
14
|
+
export const shaped = <T>(value: unknown) => value as T;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/* zod validators for JSON bodies and query strings: a failure answers 400
|
|
2
|
+
{ ok: false, error: "<campo>: <mensagem>" }, in Portuguese since it may reach the UI. */
|
|
3
|
+
|
|
4
|
+
import { zValidator } from "@hono/zod-validator";
|
|
5
|
+
import { z } from "zod";
|
|
6
|
+
|
|
7
|
+
z.config(z.locales.pt());
|
|
8
|
+
/* a missing field reads better as "obrigatório" than as "recebeu undefined" */
|
|
9
|
+
z.config({
|
|
10
|
+
customError: issue => (issue.code === "invalid_type" && issue.input === undefined ? "obrigatório" : undefined),
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
export function validationMessage(issues: readonly z.core.$ZodIssue[]): string {
|
|
14
|
+
const issue = issues[0];
|
|
15
|
+
if (!issue) return "requisição inválida";
|
|
16
|
+
const field = issue.path.map(String).join(".") || "corpo";
|
|
17
|
+
return `${field}: ${issue.message}`;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const invalid = (issues: readonly z.core.$ZodIssue[]) =>
|
|
21
|
+
Response.json({ ok: false, error: validationMessage(issues) }, { status: 400 });
|
|
22
|
+
|
|
23
|
+
export const json = <S extends z.ZodType>(schema: S) =>
|
|
24
|
+
zValidator("json", schema, r => {
|
|
25
|
+
if (!r.success) return invalid(r.error.issues);
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
export const query = <S extends z.ZodType>(schema: S) =>
|
|
29
|
+
zValidator("query", schema, r => {
|
|
30
|
+
if (!r.success) return invalid(r.error.issues);
|
|
31
|
+
});
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/* The dir of the video a route names, or the plain 404 every video route answers when it is gone. */
|
|
2
|
+
|
|
3
|
+
import { videoDirs } from "../services/videos";
|
|
4
|
+
import { notFound } from "./errors";
|
|
5
|
+
|
|
6
|
+
export function videoDir(slug: string): string {
|
|
7
|
+
const dir = videoDirs().get(slug);
|
|
8
|
+
if (!dir) throw notFound();
|
|
9
|
+
return dir;
|
|
10
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/* JSON answers keep the content-type Bun's Response.json gave them before the Hono move
|
|
2
|
+
("application/json;charset=utf-8"), so nothing downstream sees a different header. */
|
|
3
|
+
|
|
4
|
+
import { createMiddleware } from "hono/factory";
|
|
5
|
+
import type { AppEnv } from "../lib/hono";
|
|
6
|
+
|
|
7
|
+
export const jsonCharset = createMiddleware<AppEnv>(async (c, next) => {
|
|
8
|
+
await next();
|
|
9
|
+
if (c.res.headers.get("content-type") === "application/json")
|
|
10
|
+
c.res.headers.set("content-type", "application/json;charset=utf-8");
|
|
11
|
+
});
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/* Only this machine and only the studio UI. Host blocks DNS rebinding; Origin and mandatory
|
|
2
|
+
JSON block another page open in the browser from posting here. The upload paths take a raw
|
|
3
|
+
body instead: their custom x-file-name header makes the browser send a preflight from any
|
|
4
|
+
other origin, which this server doesn't answer. */
|
|
5
|
+
|
|
6
|
+
import { createMiddleware } from "hono/factory";
|
|
7
|
+
import type { AppEnv } from "../lib/hono";
|
|
8
|
+
|
|
9
|
+
export type LocalOnlyOptions = { port: number; uploadPaths?: string[] };
|
|
10
|
+
|
|
11
|
+
const text = (body: string, status: number) => new Response(body, { status });
|
|
12
|
+
|
|
13
|
+
export function rejectForeign(req: Request, hosts: Set<string>, origins: Set<string>, upload: boolean) {
|
|
14
|
+
if (!hosts.has(req.headers.get("host") ?? "")) return text("host não permitido", 403);
|
|
15
|
+
if (req.method === "GET" || req.method === "HEAD") return null;
|
|
16
|
+
const origin = req.headers.get("origin");
|
|
17
|
+
if (origin && !origins.has(origin)) return text("origem não permitida", 403);
|
|
18
|
+
if (upload) return req.headers.get("x-file-name") ? null : text("falta o nome do arquivo", 415);
|
|
19
|
+
if (!req.headers.get("content-type")?.startsWith("application/json")) return text("só JSON", 415);
|
|
20
|
+
return null;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function localOnly({ port, uploadPaths = [] }: LocalOnlyOptions) {
|
|
24
|
+
const hosts = new Set([`127.0.0.1:${port}`, `localhost:${port}`]);
|
|
25
|
+
const origins = new Set([...hosts].map(h => `http://${h}`));
|
|
26
|
+
const uploads = new Set(uploadPaths);
|
|
27
|
+
return createMiddleware<AppEnv>(async (c, next) => {
|
|
28
|
+
const rejected = rejectForeign(c.req.raw, hosts, origins, uploads.has(c.req.path));
|
|
29
|
+
if (rejected) return rejected;
|
|
30
|
+
await next();
|
|
31
|
+
});
|
|
32
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/* Long requests (Instagram reads, uploads, clone, connect, collect, streams): lifts Bun's 10 s
|
|
2
|
+
idle timeout for this request. */
|
|
3
|
+
|
|
4
|
+
import { createMiddleware } from "hono/factory";
|
|
5
|
+
import type { AppEnv } from "../lib/hono";
|
|
6
|
+
|
|
7
|
+
export const noTimeout = createMiddleware<AppEnv>(async (c, next) => {
|
|
8
|
+
c.env?.server?.timeout(c.req.raw, 0);
|
|
9
|
+
await next();
|
|
10
|
+
});
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/* The admin panel (services/admin.ts): who is looking, and Omni's settings for every user. A
|
|
2
|
+
non-admin only learns that it isn't one; a change from one is refused. */
|
|
3
|
+
|
|
4
|
+
import { json } from "../lib/validate";
|
|
5
|
+
import { router } from "../lib/hono";
|
|
6
|
+
import { omniAdmin, setOmniAdmin, viewerOf } from "../services/admin";
|
|
7
|
+
import { omniAdminBody } from "../../shared/schemas/admin";
|
|
8
|
+
|
|
9
|
+
export const admin = router()
|
|
10
|
+
.get("/admin", c => {
|
|
11
|
+
const viewer = viewerOf(c.req.raw);
|
|
12
|
+
return c.json({ ...viewer, omni: viewer.admin ? omniAdmin() : null });
|
|
13
|
+
})
|
|
14
|
+
.post("/admin/omni", json(omniAdminBody), c => {
|
|
15
|
+
if (!viewerOf(c.req.raw).admin) return c.json({ ok: false as const, error: "só o admin muda a Omni" }, 403);
|
|
16
|
+
const r = setOmniAdmin(c.req.valid("json"));
|
|
17
|
+
return r.ok ? c.json(r) : c.json(r, 400);
|
|
18
|
+
});
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
/* Omni in cascade mode (services/agent-cascade.ts): the session, the STT token to reconnect, each
|
|
2
|
+
turn streamed back as NDJSON (text, tools, staged actions, audio) and the browser's measurements. */
|
|
3
|
+
|
|
4
|
+
import { json } from "../lib/validate";
|
|
5
|
+
import { router } from "../lib/hono";
|
|
6
|
+
import { fail } from "../lib/result";
|
|
7
|
+
import { noTimeout } from "../middleware/no-timeout";
|
|
8
|
+
import { logEvent } from "../services/agent";
|
|
9
|
+
import {
|
|
10
|
+
cascadeClip,
|
|
11
|
+
commitTurn,
|
|
12
|
+
endCascade,
|
|
13
|
+
hasCascade,
|
|
14
|
+
resolveUiCall,
|
|
15
|
+
runTurn,
|
|
16
|
+
startCascade,
|
|
17
|
+
sttSocket,
|
|
18
|
+
} from "../services/agent-cascade";
|
|
19
|
+
import { emptyBody } from "../../shared/schemas/common";
|
|
20
|
+
import {
|
|
21
|
+
cascadeLogBody,
|
|
22
|
+
cascadeSessionBody,
|
|
23
|
+
cascadeTurnBody,
|
|
24
|
+
cascadeUiResultBody,
|
|
25
|
+
} from "../../shared/schemas/agent-cascade";
|
|
26
|
+
|
|
27
|
+
export const agentCascade = router()
|
|
28
|
+
.post("/agent/cascade/session", json(cascadeSessionBody), async c => {
|
|
29
|
+
try {
|
|
30
|
+
const b = c.req.valid("json");
|
|
31
|
+
return c.json({ ok: true as const, ...(await startCascade(b.mode, b.history)) });
|
|
32
|
+
} catch (e) {
|
|
33
|
+
return fail(c, String((e as Error)?.message ?? e));
|
|
34
|
+
}
|
|
35
|
+
})
|
|
36
|
+
.post("/agent/cascade/:id/stt", json(emptyBody), async c => {
|
|
37
|
+
if (!hasCascade(c.req.param("id"))) return fail(c, "sessão desconhecida");
|
|
38
|
+
try {
|
|
39
|
+
return c.json({ ok: true as const, ...(await sttSocket()) });
|
|
40
|
+
} catch (e) {
|
|
41
|
+
return fail(c, String((e as Error)?.message ?? e));
|
|
42
|
+
}
|
|
43
|
+
})
|
|
44
|
+
/* one NDJSON line per event; the page aborting the request (the user spoke over Omni) stops the turn */
|
|
45
|
+
.post("/agent/cascade/:id/turn", noTimeout, json(cascadeTurnBody), c => {
|
|
46
|
+
const id = c.req.param("id");
|
|
47
|
+
const { text, speculative, screen } = c.req.valid("json");
|
|
48
|
+
const ctrl = new AbortController();
|
|
49
|
+
c.req.raw.signal.addEventListener("abort", () => ctrl.abort());
|
|
50
|
+
const enc = new TextEncoder();
|
|
51
|
+
const body = new ReadableStream<Uint8Array>({
|
|
52
|
+
async start(stream) {
|
|
53
|
+
let open = true;
|
|
54
|
+
await runTurn(
|
|
55
|
+
id,
|
|
56
|
+
text,
|
|
57
|
+
ctrl.signal,
|
|
58
|
+
e => {
|
|
59
|
+
if (!open) return;
|
|
60
|
+
try {
|
|
61
|
+
stream.enqueue(enc.encode(`${JSON.stringify(e)}\n`));
|
|
62
|
+
} catch {
|
|
63
|
+
open = false;
|
|
64
|
+
}
|
|
65
|
+
},
|
|
66
|
+
speculative === true,
|
|
67
|
+
screen,
|
|
68
|
+
);
|
|
69
|
+
if (open) stream.close();
|
|
70
|
+
},
|
|
71
|
+
cancel() {
|
|
72
|
+
ctrl.abort();
|
|
73
|
+
},
|
|
74
|
+
});
|
|
75
|
+
return new Response(body, { headers: { "content-type": "application/x-ndjson", "cache-control": "no-store" } });
|
|
76
|
+
})
|
|
77
|
+
/* the speculative turn in flight was right: the user had finished */
|
|
78
|
+
.post("/agent/cascade/:id/commit", json(emptyBody), c => c.json({ ok: commitTurn(c.req.param("id")) }))
|
|
79
|
+
.post("/agent/cascade/:id/ui-result", json(cascadeUiResultBody), c => {
|
|
80
|
+
const b = c.req.valid("json");
|
|
81
|
+
return c.json({ ok: resolveUiCall(c.req.param("id"), b.call_id, b.output) });
|
|
82
|
+
})
|
|
83
|
+
/* "Ainda tá aí?" and the goodbye, in Omni's voice (raw PCM16 24 kHz) */
|
|
84
|
+
.get("/agent/cascade/:id/clip/:name", async c => {
|
|
85
|
+
try {
|
|
86
|
+
const pcm = await cascadeClip(c.req.param("id"), c.req.param("name"));
|
|
87
|
+
return new Response(new Blob([pcm as Uint8Array<ArrayBuffer>]), {
|
|
88
|
+
headers: { "content-type": "application/octet-stream", "cache-control": "no-store" },
|
|
89
|
+
});
|
|
90
|
+
} catch (e) {
|
|
91
|
+
return fail(c, String((e as Error)?.message ?? e));
|
|
92
|
+
}
|
|
93
|
+
})
|
|
94
|
+
.post("/agent/cascade/:id/log", json(cascadeLogBody), c => {
|
|
95
|
+
const id = c.req.param("id");
|
|
96
|
+
const b = c.req.valid("json");
|
|
97
|
+
if (b.type === "end") endCascade(id, b.duration_s, b.stt_audio_s, b.reason);
|
|
98
|
+
else logEvent({ session: id, ...b, ...(b.type === "turn" ? { mode: "cascade" } : {}) });
|
|
99
|
+
return c.json({ ok: true as const });
|
|
100
|
+
});
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/* Omni's conversation history (services/agent-history.ts): the list, one conversation, the browser's
|
|
2
|
+
save as the call goes, and deleting one. */
|
|
3
|
+
|
|
4
|
+
import { json } from "../lib/validate";
|
|
5
|
+
import { router } from "../lib/hono";
|
|
6
|
+
import { fail, result } from "../lib/result";
|
|
7
|
+
import { notFound } from "../lib/errors";
|
|
8
|
+
import { deleteConversation, getConversation, listConversations, saveConversation } from "../services/agent-history";
|
|
9
|
+
import { emptyBody } from "../../shared/schemas/common";
|
|
10
|
+
import { conversationSaveBody } from "../../shared/schemas/agent-history";
|
|
11
|
+
|
|
12
|
+
export const agentHistory = router()
|
|
13
|
+
.get("/agent/conversations", c => c.json(listConversations()))
|
|
14
|
+
.get("/agent/conversations/:id", c => {
|
|
15
|
+
const conv = getConversation(c.req.param("id"));
|
|
16
|
+
if (!conv) throw notFound();
|
|
17
|
+
return c.json(conv);
|
|
18
|
+
})
|
|
19
|
+
.post("/agent/conversations/:id", json(conversationSaveBody), c => {
|
|
20
|
+
const r = saveConversation(c.req.param("id"), c.req.valid("json"));
|
|
21
|
+
return r.ok ? c.json(r) : fail(c, r.error);
|
|
22
|
+
})
|
|
23
|
+
.post("/agent/conversations/:id/delete", json(emptyBody), c => result(c, deleteConversation(c.req.param("id"))));
|