@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,257 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/* ============================================================================
|
|
3
|
+
Parallel narration (stage 4): one fork per scene writes that scene's lines,
|
|
4
|
+
all at once.
|
|
5
|
+
|
|
6
|
+
node tools/narrate.mjs plan videos/<slug> [--only c02,c05] [--all] [--json]
|
|
7
|
+
which scenes, the model and effort, and each fork's prompt. Each scene
|
|
8
|
+
is handed out once (tools/lib/fanout.mjs): a second plan launches
|
|
9
|
+
nothing for a scene still running.
|
|
10
|
+
node tools/narrate.mjs apply videos/<slug> cNN
|
|
11
|
+
merges narration/<id>.json into scenes.json, under the scenes lock.
|
|
12
|
+
node tools/narrate.mjs check videos/<slug>
|
|
13
|
+
the stage's gate over the whole video, after the last fork: every
|
|
14
|
+
scene with lines, every beat tied to a word that exists, no line over
|
|
15
|
+
22 words. Exit 1 with the problems; the total against 60 s (9:16) is a
|
|
16
|
+
warning.
|
|
17
|
+
|
|
18
|
+
A scene fork writes only narration/<id>.json:
|
|
19
|
+
|
|
20
|
+
{ "lines": [ { "id": "c03f1", "text": "…", "instruct"?: "…" }, … ],
|
|
21
|
+
"enters_with": { "<beat element>": "c03f1:palavra", … } }
|
|
22
|
+
|
|
23
|
+
and calls `apply`, the only writer of scenes.json while the forks run. A line
|
|
24
|
+
that keeps its id and text keeps its voice (wav, dur, hash…); a changed text
|
|
25
|
+
changes the hash, and `omnivideo.py voice` redoes only that line.
|
|
26
|
+
============================================================================ */
|
|
27
|
+
|
|
28
|
+
import { readFileSync, writeFileSync, existsSync, renameSync } from "node:fs";
|
|
29
|
+
import { join, resolve, relative } from "node:path";
|
|
30
|
+
import { plan as fanout, agentModel, waves as split, printPlan, withScenesLock } from "./lib/fanout.mjs";
|
|
31
|
+
import { resolveTime } from "./timing.mjs";
|
|
32
|
+
import { isNarrated } from "./lib/narration.mjs";
|
|
33
|
+
import { DATA_ROOT as ROOT } from "./lib/paths.mjs";
|
|
34
|
+
|
|
35
|
+
const CFG = JSON.parse(readFileSync(join(ROOT, "omnivideo.config.json"), "utf8"));
|
|
36
|
+
const MAX_WORDS = 22;
|
|
37
|
+
/* the cloned voice as measured, at 1.0× (skill av-narration, Line rules) */
|
|
38
|
+
const WORDS_PER_S = 2.2;
|
|
39
|
+
|
|
40
|
+
const [cmd, target, ...rest] = process.argv.slice(2);
|
|
41
|
+
const flag = n => rest.includes(`--${n}`);
|
|
42
|
+
const opt = n => {
|
|
43
|
+
const i = rest.indexOf(`--${n}`);
|
|
44
|
+
return i < 0 ? null : rest[i + 1];
|
|
45
|
+
};
|
|
46
|
+
const usage = () => {
|
|
47
|
+
console.error(
|
|
48
|
+
"uso: node tools/narrate.mjs plan videos/<slug> [--only c02,c05] [--all] [--json]\n" +
|
|
49
|
+
" node tools/narrate.mjs apply videos/<slug> cNN\n" +
|
|
50
|
+
" node tools/narrate.mjs check videos/<slug>",
|
|
51
|
+
);
|
|
52
|
+
process.exit(2);
|
|
53
|
+
};
|
|
54
|
+
if (!cmd || !target) usage();
|
|
55
|
+
const dir = resolve(target);
|
|
56
|
+
const slug = relative(ROOT, dir);
|
|
57
|
+
if (!existsSync(join(dir, "scenes.json"))) {
|
|
58
|
+
console.error(`erro: falta ${join(dir, "scenes.json")}`);
|
|
59
|
+
process.exit(2);
|
|
60
|
+
}
|
|
61
|
+
const readScenes = () => JSON.parse(readFileSync(join(dir, "scenes.json"), "utf8"));
|
|
62
|
+
const fail = msg => {
|
|
63
|
+
console.error(`erro: ${msg}`);
|
|
64
|
+
process.exit(1);
|
|
65
|
+
};
|
|
66
|
+
const wordCount = text =>
|
|
67
|
+
String(text ?? "")
|
|
68
|
+
.trim()
|
|
69
|
+
.split(/\s+/)
|
|
70
|
+
.filter(Boolean).length;
|
|
71
|
+
|
|
72
|
+
/* a ref checked against the scene's lines: the line exists and so does the word */
|
|
73
|
+
function refProblem(scene, ref) {
|
|
74
|
+
const probe = { ...scene, lines: (scene.lines ?? []).map(l => ({ id: l.id, text: l.text, start: 0, end: 1 })) };
|
|
75
|
+
try {
|
|
76
|
+
resolveTime(probe, ref);
|
|
77
|
+
return null;
|
|
78
|
+
} catch (e) {
|
|
79
|
+
return e.message;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/* ---------- plan ---------------------------------------------------------- */
|
|
84
|
+
|
|
85
|
+
async function plan() {
|
|
86
|
+
if (!isNarrated(dir)) fail("este vídeo é sem narração e voz (content.json): não há narração a escrever");
|
|
87
|
+
const video = readScenes();
|
|
88
|
+
const ids = video.scenes.map(s => s.id);
|
|
89
|
+
const only = opt("only")?.split(",").filter(Boolean) ?? null;
|
|
90
|
+
if (only) {
|
|
91
|
+
const outside = only.filter(id => !ids.includes(id));
|
|
92
|
+
if (outside.length) fail(`não são cenas do scenes.json: ${outside.join(", ")}`);
|
|
93
|
+
}
|
|
94
|
+
const noBeats = video.scenes.filter(s => !s.beats?.length).map(s => s.id);
|
|
95
|
+
if (noBeats.length) fail(`cenas sem beats (componha antes, etapa 3): ${noBeats.join(", ")}`);
|
|
96
|
+
const wanted = only ?? (flag("all") ? ids : video.scenes.filter(s => !s.lines?.length).map(s => s.id));
|
|
97
|
+
const stage = CFG.models?.stages?.["av-narration"] ?? { model: "haiku", effort: "high" };
|
|
98
|
+
const parallel = Math.max(1, CFG.narration?.parallel ?? CFG.motion?.compose?.parallel ?? 8);
|
|
99
|
+
const model = agentModel(stage.model),
|
|
100
|
+
effort = stage.effort ?? "high";
|
|
101
|
+
if (!wanted.length) {
|
|
102
|
+
if (flag("json")) console.log(JSON.stringify({ video: slug, run: null, waves: [], busy: [] }, null, 2));
|
|
103
|
+
else console.log(`nada a narrar: toda cena já tem falas. Próximo: node tools/narrate.mjs check ${slug}`);
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
const { run, planned, busy } = await fanout(dir, "narration", wanted);
|
|
107
|
+
const prompt = id =>
|
|
108
|
+
`Você é um fork da skill av-narration. Leia .claude/skills/av-narration/SKILL.md e siga-a ` +
|
|
109
|
+
`com este pedido: ${slug} narrar ${id} (paralelo, rodada ${run}). Termine com o relatório curto que a skill pede.`;
|
|
110
|
+
const ws = split(planned, parallel);
|
|
111
|
+
if (flag("json")) {
|
|
112
|
+
console.log(
|
|
113
|
+
JSON.stringify(
|
|
114
|
+
{
|
|
115
|
+
video: slug,
|
|
116
|
+
run,
|
|
117
|
+
model,
|
|
118
|
+
effort,
|
|
119
|
+
busy,
|
|
120
|
+
waves: ws.map(w => w.map(id => ({ scene: id, description: `narrar ${id}`, prompt: prompt(id) }))),
|
|
121
|
+
},
|
|
122
|
+
null,
|
|
123
|
+
2,
|
|
124
|
+
),
|
|
125
|
+
);
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
printPlan({
|
|
129
|
+
title: `narração em paralelo · ${planned.length} cena(s) nesta rodada · até ${parallel} por vez · modelo ${model}, esforço ${effort}`,
|
|
130
|
+
run,
|
|
131
|
+
waves: ws,
|
|
132
|
+
model,
|
|
133
|
+
effort,
|
|
134
|
+
prompt,
|
|
135
|
+
busy,
|
|
136
|
+
after: `Depois da última onda: node tools/narrate.mjs check ${slug} (o gate da etapa).`,
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/* ---------- apply --------------------------------------------------------- */
|
|
141
|
+
|
|
142
|
+
function writeAtomic(file, text) {
|
|
143
|
+
const tmp = `${file}.${process.pid}.tmp`;
|
|
144
|
+
writeFileSync(tmp, text);
|
|
145
|
+
renameSync(tmp, file);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
async function apply() {
|
|
149
|
+
const id = rest[0];
|
|
150
|
+
if (!id) usage();
|
|
151
|
+
const file = join(dir, "narration", `${id}.json`);
|
|
152
|
+
if (!existsSync(file)) fail(`falta ${relative(ROOT, file)}`);
|
|
153
|
+
let p;
|
|
154
|
+
try {
|
|
155
|
+
p = JSON.parse(readFileSync(file, "utf8"));
|
|
156
|
+
} catch (e) {
|
|
157
|
+
fail(`${relative(ROOT, file)} não é JSON válido: ${e.message}`);
|
|
158
|
+
}
|
|
159
|
+
await withScenesLock(dir, () => {
|
|
160
|
+
const video = readScenes();
|
|
161
|
+
const scene = video.scenes.find(s => s.id === id);
|
|
162
|
+
if (!scene) throw new Error(`${id} não existe em scenes.json`);
|
|
163
|
+
const errors = [],
|
|
164
|
+
warnings = [];
|
|
165
|
+
if (!Array.isArray(p.lines) || !p.lines.length) errors.push("lines precisa ser uma lista não vazia");
|
|
166
|
+
const seen = new Set();
|
|
167
|
+
for (const l of p.lines ?? []) {
|
|
168
|
+
if (!l?.id || !new RegExp(`^${id}f\\d+$`).test(l.id))
|
|
169
|
+
errors.push(`id de fala inválido: ${l?.id} (use ${id}f1, ${id}f2…)`);
|
|
170
|
+
else if (seen.has(l.id)) errors.push(`fala repetida: ${l.id}`);
|
|
171
|
+
seen.add(l?.id);
|
|
172
|
+
if (!String(l?.text ?? "").trim()) errors.push(`${l?.id}: texto vazio`);
|
|
173
|
+
else if (wordCount(l.text) > MAX_WORDS)
|
|
174
|
+
errors.push(`${l.id}: ${wordCount(l.text)} palavras (máximo ${MAX_WORDS})`);
|
|
175
|
+
}
|
|
176
|
+
const ew = p.enters_with ?? {};
|
|
177
|
+
const elements = new Set((scene.beats ?? []).map(b => b.element));
|
|
178
|
+
for (const el of Object.keys(ew))
|
|
179
|
+
if (!elements.has(el)) errors.push(`enters_with de um beat que não existe: ${el}`);
|
|
180
|
+
if (errors.length) throw new Error(errors.map(e => `${id}: ${e}`).join("\nerro: "));
|
|
181
|
+
|
|
182
|
+
/* a line that keeps its id keeps what the voice stage wrote on it */
|
|
183
|
+
const old = Object.fromEntries((scene.lines ?? []).map(l => [l.id, l]));
|
|
184
|
+
scene.lines = p.lines.map(l => {
|
|
185
|
+
const prev = old[l.id];
|
|
186
|
+
const next = { id: l.id, text: l.text.trim(), ...(l.instruct ? { instruct: l.instruct } : {}) };
|
|
187
|
+
return prev ? { ...prev, ...next, ...(l.instruct ? {} : { instruct: undefined }) } : next;
|
|
188
|
+
});
|
|
189
|
+
for (const l of scene.lines) if (l.instruct === undefined) delete l.instruct;
|
|
190
|
+
for (const b of scene.beats ?? []) {
|
|
191
|
+
if (ew[b.element] != null) b.enters_with = ew[b.element];
|
|
192
|
+
if (b.enters_with == null) warnings.push(`beat ${b.element} sem enters_with`);
|
|
193
|
+
else {
|
|
194
|
+
const problem = refProblem(scene, b.enters_with);
|
|
195
|
+
if (problem) errors.push(`beat ${b.element}: ${problem}`);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
if (errors.length) throw new Error(errors.map(e => `${id}: ${e}`).join("\nerro: "));
|
|
199
|
+
writeAtomic(join(dir, "scenes.json"), JSON.stringify(video, null, 2) + "\n");
|
|
200
|
+
const words = scene.lines.reduce((n, l) => n + wordCount(l.text), 0);
|
|
201
|
+
console.log(
|
|
202
|
+
`${id} · ${scene.lines.length} fala(s), ${words} palavras · ${Object.keys(ew).length} beat(s) ligados → scenes.json`,
|
|
203
|
+
);
|
|
204
|
+
warnings.forEach(w => console.log(`aviso: ${id}: ${w}`));
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/* ---------- check --------------------------------------------------------- */
|
|
209
|
+
|
|
210
|
+
function check() {
|
|
211
|
+
const video = readScenes();
|
|
212
|
+
const problems = [],
|
|
213
|
+
notes = [];
|
|
214
|
+
let words = 0;
|
|
215
|
+
for (const s of video.scenes) {
|
|
216
|
+
if (!s.lines?.length) {
|
|
217
|
+
problems.push(`${s.id}: sem falas`);
|
|
218
|
+
continue;
|
|
219
|
+
}
|
|
220
|
+
for (const l of s.lines) {
|
|
221
|
+
words += wordCount(l.text);
|
|
222
|
+
if (!String(l.text ?? "").trim()) problems.push(`${l.id}: texto vazio`);
|
|
223
|
+
else if (wordCount(l.text) > MAX_WORDS)
|
|
224
|
+
problems.push(`${l.id}: ${wordCount(l.text)} palavras (máximo ${MAX_WORDS})`);
|
|
225
|
+
}
|
|
226
|
+
for (const b of s.beats ?? []) {
|
|
227
|
+
if (b.enters_with == null) problems.push(`${s.id}: beat ${b.element} sem enters_with`);
|
|
228
|
+
else {
|
|
229
|
+
const problem = refProblem(s, b.enters_with);
|
|
230
|
+
if (problem) problems.push(`${s.id}: beat ${b.element}: ${problem}`);
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
const speed = Number(video.voice?.speed ?? CFG.voice?.fish?.speed ?? 1) || 1;
|
|
235
|
+
const est = words / (WORDS_PER_S * speed);
|
|
236
|
+
const portrait = (video.format?.h ?? 1920) > (video.format?.w ?? 1080);
|
|
237
|
+
notes.push(`${words} palavras ≈ ${est.toFixed(0)} s de voz (${WORDS_PER_S} palavras/s × velocidade ${speed})`);
|
|
238
|
+
/* a warning, not a failure: the duration in script.md may ask for more, and 2.2 words/s is the slow end */
|
|
239
|
+
if (portrait && est > 60)
|
|
240
|
+
notes.push(
|
|
241
|
+
`aviso: ≈ ${est.toFixed(0)} s de voz passa de 60 s (9:16): confira a duração pedida no script.md; se passou, corte falas, não cenas`,
|
|
242
|
+
);
|
|
243
|
+
notes.forEach(n => console.log(n));
|
|
244
|
+
if (problems.length) {
|
|
245
|
+
console.error(`erro: o gate da narração não passa:\n ${problems.join("\n ")}`);
|
|
246
|
+
process.exit(1);
|
|
247
|
+
}
|
|
248
|
+
console.log("narração ok: toda cena com falas e todo beat ligado a uma palavra que existe");
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
if (cmd === "plan" || cmd === "apply")
|
|
252
|
+
await (cmd === "plan" ? plan() : apply()).catch(e => {
|
|
253
|
+
console.error(`erro: ${e.message}`);
|
|
254
|
+
process.exit(1);
|
|
255
|
+
});
|
|
256
|
+
else if (cmd === "check") check();
|
|
257
|
+
else usage();
|
package/tools/omni.mjs
ADDED
|
@@ -0,0 +1,298 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/* ============================================================================
|
|
3
|
+
The `omni` CLI (docs/specs/phase-2-plugin-cli.md): `npx -y @crievo/omni …` once published (bin/omni.mjs
|
|
4
|
+
settles the workspace and calls main), `node tools/omni.mjs …` in this repo. The plugin calls it from its
|
|
5
|
+
hook, its MCP server and its skills' `!` lines, so the user needs no terminal. The login is OAuth with PKCE
|
|
6
|
+
in the browser (tools/lib/oauth-client.mjs); the tokens live in the macOS Keychain, never in .env.
|
|
7
|
+
|
|
8
|
+
omni up [--hook] readies this machine: workspace, app, Python, login, studio service
|
|
9
|
+
(tools/lib/up.mjs); --hook prints one line and never waits on the login
|
|
10
|
+
omni login [--name <nome>] enters through the browser: approve, and this computer is in
|
|
11
|
+
omni status [--json] the account, the plan, the credits and this computer
|
|
12
|
+
omni logout signs this computer out in the cloud and here
|
|
13
|
+
omni allow [--yes] the Claude Code permission rules the skills need (asks first)
|
|
14
|
+
omni setup login, the rules and up, for whoever starts from a terminal
|
|
15
|
+
omni doctor the system programs and keys (tools/setup.mjs check)
|
|
16
|
+
omni skill <nome> [--v <versão>] [--part i/n]
|
|
17
|
+
one part of a skill's body from the registry (tools/lib/skill-client.mjs)
|
|
18
|
+
omni run <ferramenta> [args…]
|
|
19
|
+
a pipeline tool against the workspace, what the served skills call
|
|
20
|
+
omni mcp the plugin's MCP server over stdio (tools/lib/mcp-server.mjs)
|
|
21
|
+
============================================================================ */
|
|
22
|
+
|
|
23
|
+
import { spawnSync } from "node:child_process";
|
|
24
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
25
|
+
import { hostname } from "node:os";
|
|
26
|
+
import { join } from "node:path";
|
|
27
|
+
import { createInterface } from "node:readline/promises";
|
|
28
|
+
import { allowSkillRules, missingRules, settingsFile } from "./lib/claude-settings.mjs";
|
|
29
|
+
import { CLI_COMMAND, STUDIO_URL } from "./lib/cli.mjs";
|
|
30
|
+
import { serveMcp } from "./lib/mcp-server.mjs";
|
|
31
|
+
import { accessToken, login, logout, readTokens } from "./lib/oauth-client.mjs";
|
|
32
|
+
import { CODE_ROOT, DATA_ROOT, isMainModule } from "./lib/paths.mjs";
|
|
33
|
+
import { service } from "./lib/service.mjs";
|
|
34
|
+
import { skillText } from "./lib/skill-client.mjs";
|
|
35
|
+
import { up } from "./lib/up.mjs";
|
|
36
|
+
import { cloudSettings } from "./platforms.mjs";
|
|
37
|
+
|
|
38
|
+
const fail = (/** @type {string} */ m) => {
|
|
39
|
+
console.error(`erro: ${m}`);
|
|
40
|
+
process.exit(1);
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
const studioUrl = () => cloudSettings().studio.url || STUDIO_URL;
|
|
44
|
+
const deviceName = () => hostname().replace(/\.local$/, "");
|
|
45
|
+
const version = () => {
|
|
46
|
+
try {
|
|
47
|
+
return JSON.parse(readFileSync(join(CODE_ROOT, "package.json"), "utf8")).version ?? "dev";
|
|
48
|
+
} catch {
|
|
49
|
+
return "dev";
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
/** the account as the studio sees it, or why not @param {string} url */
|
|
54
|
+
async function account(url) {
|
|
55
|
+
const token = await accessToken(url);
|
|
56
|
+
if (!token) return { logged_in: false };
|
|
57
|
+
const r = await fetch(`${url}/account`, { headers: { authorization: `Bearer ${token}` } });
|
|
58
|
+
const a = /** @type {any} */ (await r.json().catch(() => ({})));
|
|
59
|
+
if (!r.ok) throw new Error(a.error ?? `o studio respondeu ${r.status}`);
|
|
60
|
+
return { logged_in: true, ...a };
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/** @param {any} a */
|
|
64
|
+
const planText = a =>
|
|
65
|
+
a.active
|
|
66
|
+
? `${a.plan} (ativo)`
|
|
67
|
+
: a.plan
|
|
68
|
+
? `${a.plan} (${a.status === "past_due" ? "pagamento atrasado" : "cancelado"})`
|
|
69
|
+
: "sem plano";
|
|
70
|
+
|
|
71
|
+
/** one paragraph for the model: what works and what is missing @param {string} url */
|
|
72
|
+
async function statusText(url) {
|
|
73
|
+
const lines = [];
|
|
74
|
+
try {
|
|
75
|
+
const a = await account(url);
|
|
76
|
+
lines.push(
|
|
77
|
+
a.logged_in
|
|
78
|
+
? `Logado como ${a.email} neste computador (${a.device?.name}); plano ${planText(a)}; ${a.credits} créditos.`
|
|
79
|
+
: "Este computador não está logado no OmniVideo: chame omni_login.",
|
|
80
|
+
);
|
|
81
|
+
} catch (e) {
|
|
82
|
+
lines.push(`O studio do OmniVideo não respondeu: ${/** @type {Error} */ (e).message}.`);
|
|
83
|
+
}
|
|
84
|
+
const s = service();
|
|
85
|
+
lines.push(
|
|
86
|
+
s.loaded
|
|
87
|
+
? "O studio deste computador está ligado e atende o studio hospedado."
|
|
88
|
+
: "O studio deste computador está desligado.",
|
|
89
|
+
);
|
|
90
|
+
const rules = missingRules();
|
|
91
|
+
lines.push(
|
|
92
|
+
rules.length
|
|
93
|
+
? "As skills do OmniVideo ainda não têm permissão para rodar sem pedir aprovação: pergunte ao usuário se pode liberar e, com um sim, chame omni_allow_skills."
|
|
94
|
+
: "As skills do OmniVideo têm permissão para rodar.",
|
|
95
|
+
);
|
|
96
|
+
lines.push(`Studio hospedado: ${url}`);
|
|
97
|
+
return lines.join("\n");
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/** the plugin's MCP tools @param {string} url */
|
|
101
|
+
const mcpTools = url => [
|
|
102
|
+
{
|
|
103
|
+
name: "omni_status",
|
|
104
|
+
description:
|
|
105
|
+
"Estado do OmniVideo neste computador: login, plano, créditos, o studio local e as permissões das skills. Use quando o usuário perguntar do OmniVideo, antes de começar um vídeo, ou quando uma skill do OmniVideo falhar.",
|
|
106
|
+
inputSchema: { type: "object", properties: {} },
|
|
107
|
+
run: () => statusText(url),
|
|
108
|
+
},
|
|
109
|
+
{
|
|
110
|
+
name: "omni_login",
|
|
111
|
+
description:
|
|
112
|
+
"Abre o navegador para o usuário entrar no OmniVideo e aprovar este computador, e espera ele terminar (até 10 minutos). Use quando omni_status disser que falta entrar.",
|
|
113
|
+
inputSchema: { type: "object", properties: {} },
|
|
114
|
+
run: async () => {
|
|
115
|
+
const t = await login(url, { deviceName: deviceName() });
|
|
116
|
+
return `Pronto: "${t.device_name}" entrou no OmniVideo. O studio deste computador passa a usar o login em até 30 segundos.`;
|
|
117
|
+
},
|
|
118
|
+
},
|
|
119
|
+
{
|
|
120
|
+
name: "omni_allow_skills",
|
|
121
|
+
description:
|
|
122
|
+
"Grava nas settings do Claude Code as regras que deixam as skills do OmniVideo buscar o próprio texto e rodar as ferramentas do pipeline sem pedir aprovação a cada uso. SÓ chame depois de perguntar ao usuário, em português, e ele responder que sim.",
|
|
123
|
+
inputSchema: { type: "object", properties: {} },
|
|
124
|
+
run: () => {
|
|
125
|
+
const added = allowSkillRules();
|
|
126
|
+
return added.length
|
|
127
|
+
? `Regras gravadas em ${settingsFile()}:\n${added.map(r => `- ${r}`).join("\n")}\nValem a partir da próxima skill.`
|
|
128
|
+
: "As regras já estavam gravadas.";
|
|
129
|
+
},
|
|
130
|
+
},
|
|
131
|
+
{
|
|
132
|
+
name: "omni_skill",
|
|
133
|
+
description:
|
|
134
|
+
"O texto de uma skill do OmniVideo, direto do registro. Use só quando a skill apareceu como um comando em vez de instruções (shell das skills desligado).",
|
|
135
|
+
inputSchema: {
|
|
136
|
+
type: "object",
|
|
137
|
+
properties: {
|
|
138
|
+
name: { type: "string", description: "o nome da skill, como av-motion" },
|
|
139
|
+
part: { type: "integer", description: "a parte, quando a skill tem mais de uma (padrão 1)" },
|
|
140
|
+
version: { type: "string", description: "a versão que o cabeçalho da skill indica" },
|
|
141
|
+
},
|
|
142
|
+
required: ["name"],
|
|
143
|
+
},
|
|
144
|
+
run: (/** @type {{ name: string, part?: number, version?: string }} */ a) =>
|
|
145
|
+
skillText({
|
|
146
|
+
studioUrl: url,
|
|
147
|
+
name: a.name,
|
|
148
|
+
version: a.version,
|
|
149
|
+
part: a.part ?? 1,
|
|
150
|
+
codeRoot: CODE_ROOT,
|
|
151
|
+
token: () => accessToken(url),
|
|
152
|
+
}),
|
|
153
|
+
},
|
|
154
|
+
{
|
|
155
|
+
name: "omni_studio",
|
|
156
|
+
description: "O endereço do studio hospedado do OmniVideo, ou de um vídeo nele, para mandar ao usuário.",
|
|
157
|
+
inputSchema: {
|
|
158
|
+
type: "object",
|
|
159
|
+
properties: { slug: { type: "string", description: "a pasta do vídeo, como 2026-10-18-tema" } },
|
|
160
|
+
},
|
|
161
|
+
run: (/** @type {{ slug?: string }} */ a) => (a.slug ? `${url}/videos/${encodeURIComponent(a.slug)}` : url),
|
|
162
|
+
},
|
|
163
|
+
];
|
|
164
|
+
|
|
165
|
+
/** asks yes or no on the terminal; no terminal is a no @param {string} question */
|
|
166
|
+
async function confirm(question) {
|
|
167
|
+
if (!process.stdin.isTTY) return false;
|
|
168
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
169
|
+
const answer = (await rl.question(`${question} [s/N] `)).trim().toLowerCase();
|
|
170
|
+
rl.close();
|
|
171
|
+
return answer === "s" || answer === "sim" || answer === "y";
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/** @param {string[]} args */
|
|
175
|
+
export async function main(args) {
|
|
176
|
+
const flag = (/** @type {string} */ name) => {
|
|
177
|
+
const i = args.indexOf(`--${name}`);
|
|
178
|
+
return i < 0 ? undefined : (args[i + 1] ?? "");
|
|
179
|
+
};
|
|
180
|
+
const [cmd] = args;
|
|
181
|
+
|
|
182
|
+
/* run needs no studio: it is the pipeline on this machine */
|
|
183
|
+
if (cmd === "run") {
|
|
184
|
+
const [, tool, ...rest] = args;
|
|
185
|
+
if (!tool || !/^[a-z0-9_-]+$/i.test(tool)) fail("uso: omni run <ferramenta> [args…]");
|
|
186
|
+
const mjs = join(CODE_ROOT, "tools", `${tool}.mjs`);
|
|
187
|
+
const py = join(CODE_ROOT, "tools", `${tool.replace(/-/g, "_")}.py`);
|
|
188
|
+
const venv = join(CODE_ROOT, ".venv", "bin", "python");
|
|
189
|
+
const [bin, script] = existsSync(mjs)
|
|
190
|
+
? [process.execPath, mjs]
|
|
191
|
+
: existsSync(py)
|
|
192
|
+
? [existsSync(venv) ? venv : "python3", py]
|
|
193
|
+
: [];
|
|
194
|
+
if (!bin) fail(`não existe a ferramenta ${tool}`);
|
|
195
|
+
const r = spawnSync(/** @type {string} */ (bin), [/** @type {string} */ (script), ...rest], {
|
|
196
|
+
stdio: "inherit",
|
|
197
|
+
env: { ...process.env, OMNI_HOME: DATA_ROOT },
|
|
198
|
+
});
|
|
199
|
+
process.exit(r.status ?? 1);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
if (cmd === "doctor") {
|
|
203
|
+
const r = spawnSync(process.execPath, [join(CODE_ROOT, "tools", "setup.mjs"), "check"], {
|
|
204
|
+
stdio: "inherit",
|
|
205
|
+
env: { ...process.env, OMNI_HOME: DATA_ROOT },
|
|
206
|
+
});
|
|
207
|
+
process.exit(r.status ?? 1);
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
const url = studioUrl();
|
|
211
|
+
|
|
212
|
+
if (cmd === "skill") {
|
|
213
|
+
const name = args[1] ?? "";
|
|
214
|
+
const [part] = String(flag("part") ?? "1").split("/");
|
|
215
|
+
/* always text and exit 0: Claude Code puts this output where the skill's instructions go */
|
|
216
|
+
console.log(
|
|
217
|
+
await skillText({
|
|
218
|
+
studioUrl: url,
|
|
219
|
+
name,
|
|
220
|
+
version: flag("v"),
|
|
221
|
+
part: Number(part) || 1,
|
|
222
|
+
codeRoot: CODE_ROOT,
|
|
223
|
+
token: () => accessToken(url),
|
|
224
|
+
}),
|
|
225
|
+
);
|
|
226
|
+
return;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
if (cmd === "mcp") return serveMcp({ name: "omnivideo", version: version(), tools: mcpTools(url) });
|
|
230
|
+
|
|
231
|
+
if (cmd === "up") {
|
|
232
|
+
const hook = args.includes("--hook");
|
|
233
|
+
try {
|
|
234
|
+
const r = await up({ hook, log: line => !hook && console.log(line) });
|
|
235
|
+
console.log(r.line);
|
|
236
|
+
} catch (e) {
|
|
237
|
+
/* a hook never fails the session: it says what went wrong and exits clean */
|
|
238
|
+
console.log(`OmniVideo: não consegui preparar este computador (${/** @type {Error} */ (e).message}).`);
|
|
239
|
+
if (!hook) process.exit(1);
|
|
240
|
+
}
|
|
241
|
+
return;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
if (cmd === "allow") {
|
|
245
|
+
const rules = missingRules();
|
|
246
|
+
if (!rules.length) return console.log("As regras das skills do OmniVideo já estão nas settings do Claude Code.");
|
|
247
|
+
console.log(
|
|
248
|
+
`As skills do OmniVideo precisam destas regras em ${settingsFile()}:\n${rules.map(r => ` ${r}`).join("\n")}`,
|
|
249
|
+
);
|
|
250
|
+
if (!args.includes("--yes") && !(await confirm("Gravar?"))) return console.log("Nada gravado.");
|
|
251
|
+
allowSkillRules();
|
|
252
|
+
return console.log("Regras gravadas.");
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
if (cmd === "login" || cmd === "setup") {
|
|
256
|
+
if (cmd === "login" || !readTokens(url)) {
|
|
257
|
+
const t = await login(url, { deviceName: flag("name") || deviceName(), log: line => console.log(line) }).catch(
|
|
258
|
+
e => fail(String(e.message)),
|
|
259
|
+
);
|
|
260
|
+
console.log(`Pronto: "${t.device_name}" entrou no OmniVideo. O login ficou no Keychain.`);
|
|
261
|
+
}
|
|
262
|
+
if (cmd === "login") return console.log("O studio deste computador passa a usar este login em até 30 segundos.");
|
|
263
|
+
if (
|
|
264
|
+
missingRules().length &&
|
|
265
|
+
(args.includes("--yes") || (await confirm("Liberar as skills do OmniVideo nas settings do Claude Code?")))
|
|
266
|
+
)
|
|
267
|
+
allowSkillRules();
|
|
268
|
+
console.log((await up({ log: line => console.log(line) })).line);
|
|
269
|
+
return console.log("Para usar no Claude Code: claude plugin install omnivideo --marketplace pedroflp/omni-plugin");
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
if (cmd === "logout") {
|
|
273
|
+
if (!readTokens(url)) return console.log("Este computador não está logado.");
|
|
274
|
+
const confirmed = await logout(url);
|
|
275
|
+
return console.log(
|
|
276
|
+
confirmed
|
|
277
|
+
? "Este computador saiu do OmniVideo."
|
|
278
|
+
: "O login foi apagado aqui; o studio não respondeu, então confira em Dispositivos.",
|
|
279
|
+
);
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
if (cmd === "status") {
|
|
283
|
+
const a = await account(url).catch(e => fail(`o studio não respondeu: ${e.message}`));
|
|
284
|
+
if (args.includes("--json")) return console.log(JSON.stringify(a));
|
|
285
|
+
if (!a.logged_in) return console.log(`Este computador não está logado: ${CLI_COMMAND} login`);
|
|
286
|
+
console.log(`conta: ${a.email}`);
|
|
287
|
+
console.log(`computador: ${a.device?.name}`);
|
|
288
|
+
console.log(`plano: ${planText(a)}`);
|
|
289
|
+
console.log(`créditos: ${a.credits}`);
|
|
290
|
+
return;
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
fail(
|
|
294
|
+
"uso: omni up [--hook] | login | status [--json] | logout | allow [--yes] | setup | doctor | skill <nome> | run <ferramenta> … | mcp",
|
|
295
|
+
);
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
if (isMainModule(import.meta.url)) await main(process.argv.slice(2));
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
"""The two roots the Python tools work from, the same as tools/lib/paths.mjs.
|
|
2
|
+
|
|
3
|
+
CODE_ROOT is this repo (or the folder the `omni` CLI extracts its tools into; OMNI_CODE_ROOT points it
|
|
4
|
+
elsewhere). DATA_ROOT is the workspace with videos/, omnivideo.config.json, .env and the dot folders:
|
|
5
|
+
OMNI_HOME, or the code root when it is not set.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import os
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
|
|
11
|
+
CODE_ROOT = Path(os.environ.get("OMNI_CODE_ROOT") or Path(__file__).resolve().parent.parent).resolve()
|
|
12
|
+
DATA_ROOT = Path(os.environ.get("OMNI_HOME") or CODE_ROOT).resolve()
|