vidspotai-shared 1.0.82 → 1.0.83
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/lib/globals/aiModels/enums.d.ts +11 -1
- package/lib/globals/aiModels/enums.d.ts.map +1 -1
- package/lib/globals/aiModels/enums.js +24 -1
- package/lib/globals/aiModels/index.d.ts.map +1 -1
- package/lib/globals/aiModels/index.js +2 -0
- package/lib/globals/aiModels/providers/anthropic.d.ts +12 -0
- package/lib/globals/aiModels/providers/anthropic.d.ts.map +1 -0
- package/lib/globals/aiModels/providers/anthropic.js +88 -0
- package/lib/globals/aiModels/providers/google.d.ts.map +1 -1
- package/lib/globals/aiModels/providers/google.js +82 -0
- package/lib/globals/aiModels/providers/openai.d.ts.map +1 -1
- package/lib/globals/aiModels/providers/openai.js +63 -4
- package/lib/globals/aiModels/tierHelpers.d.ts +12 -0
- package/lib/globals/aiModels/tierHelpers.d.ts.map +1 -1
- package/lib/globals/aiModels/tierHelpers.js +83 -0
- package/lib/globals/aiModels/types.d.ts +19 -0
- package/lib/globals/aiModels/types.d.ts.map +1 -1
- package/lib/globals/types.d.ts +120 -1
- package/lib/globals/types.d.ts.map +1 -1
- package/lib/globals/types.js +135 -1
- package/lib/models/agent.model.d.ts +41 -1
- package/lib/models/agent.model.d.ts.map +1 -1
- package/lib/models/index.d.ts +1 -0
- package/lib/models/index.d.ts.map +1 -1
- package/lib/models/index.js +1 -0
- package/lib/models/social.model.d.ts +180 -0
- package/lib/models/social.model.d.ts.map +1 -0
- package/lib/models/social.model.js +2 -0
- package/lib/models/user.model.d.ts +10 -0
- package/lib/models/user.model.d.ts.map +1 -1
- package/lib/models/video.model.d.ts +6 -0
- package/lib/models/video.model.d.ts.map +1 -1
- package/lib/schemas/brief.schema.d.ts +46 -0
- package/lib/schemas/brief.schema.d.ts.map +1 -1
- package/lib/schemas/brief.schema.js +72 -1
- package/lib/schemas/index.d.ts +1 -0
- package/lib/schemas/index.d.ts.map +1 -1
- package/lib/schemas/index.js +1 -0
- package/lib/schemas/project.schema.d.ts +67 -0
- package/lib/schemas/project.schema.d.ts.map +1 -1
- package/lib/schemas/project.schema.js +12 -0
- package/lib/schemas/social.schema.d.ts +91 -0
- package/lib/schemas/social.schema.d.ts.map +1 -0
- package/lib/schemas/social.schema.js +114 -0
- package/lib/schemas/videoPlan.schema.d.ts +114 -0
- package/lib/schemas/videoPlan.schema.d.ts.map +1 -1
- package/lib/schemas/videoPlan.schema.js +141 -1
- package/lib/services/agent/chatAgent.d.ts +25 -1
- package/lib/services/agent/chatAgent.d.ts.map +1 -1
- package/lib/services/agent/chatAgent.js +145 -9
- package/lib/services/agent/costPreflight.d.ts +11 -1
- package/lib/services/agent/costPreflight.d.ts.map +1 -1
- package/lib/services/agent/costPreflight.js +18 -1
- package/lib/services/agent/covers/coverPlanner.d.ts +41 -0
- package/lib/services/agent/covers/coverPlanner.d.ts.map +1 -0
- package/lib/services/agent/covers/coverPlanner.js +278 -0
- package/lib/services/agent/covers/covers.schema.d.ts +158 -0
- package/lib/services/agent/covers/covers.schema.d.ts.map +1 -0
- package/lib/services/agent/covers/covers.schema.js +166 -0
- package/lib/services/agent/covers/index.d.ts +3 -0
- package/lib/services/agent/covers/index.d.ts.map +1 -0
- package/lib/services/agent/covers/index.js +18 -0
- package/lib/services/agent/critic.d.ts +10 -0
- package/lib/services/agent/critic.d.ts.map +1 -1
- package/lib/services/agent/critic.js +37 -1
- package/lib/services/agent/editClassifier.d.ts +2 -2
- package/lib/services/agent/editClassifier.js +2 -2
- package/lib/services/agent/editExisting/editAssembler.d.ts +78 -0
- package/lib/services/agent/editExisting/editAssembler.d.ts.map +1 -0
- package/lib/services/agent/editExisting/editAssembler.js +172 -0
- package/lib/services/agent/editExisting/editExisting.schema.d.ts +119 -0
- package/lib/services/agent/editExisting/editExisting.schema.d.ts.map +1 -0
- package/lib/services/agent/editExisting/editExisting.schema.js +157 -0
- package/lib/services/agent/editExisting/highlightPicker.d.ts +48 -0
- package/lib/services/agent/editExisting/highlightPicker.d.ts.map +1 -0
- package/lib/services/agent/editExisting/highlightPicker.js +199 -0
- package/lib/services/agent/editExisting/index.d.ts +4 -0
- package/lib/services/agent/editExisting/index.d.ts.map +1 -0
- package/lib/services/agent/editExisting/index.js +19 -0
- package/lib/services/agent/eval/seedBriefs.d.ts +4 -3
- package/lib/services/agent/eval/seedBriefs.d.ts.map +1 -1
- package/lib/services/agent/eval/seedBriefs.js +283 -3
- package/lib/services/agent/eval/types.d.ts +10 -0
- package/lib/services/agent/eval/types.d.ts.map +1 -1
- package/lib/services/agent/executor/core.d.ts +70 -0
- package/lib/services/agent/executor/core.d.ts.map +1 -0
- package/lib/services/agent/executor/core.js +250 -0
- package/lib/services/agent/executor/duration.d.ts +20 -0
- package/lib/services/agent/executor/duration.d.ts.map +1 -0
- package/lib/services/agent/executor/duration.js +46 -0
- package/lib/services/agent/executor/index.d.ts +15 -0
- package/lib/services/agent/executor/index.d.ts.map +1 -0
- package/lib/services/agent/executor/index.js +32 -0
- package/lib/services/agent/executor/types.d.ts +183 -0
- package/lib/services/agent/executor/types.d.ts.map +1 -0
- package/lib/services/agent/executor/types.js +29 -0
- package/lib/services/agent/executor/visual.d.ts +32 -0
- package/lib/services/agent/executor/visual.d.ts.map +1 -0
- package/lib/services/agent/executor/visual.js +400 -0
- package/lib/services/agent/executor/voice.d.ts +17 -0
- package/lib/services/agent/executor/voice.d.ts.map +1 -0
- package/lib/services/agent/executor/voice.js +119 -0
- package/lib/services/agent/extendChain.d.ts +101 -0
- package/lib/services/agent/extendChain.d.ts.map +1 -0
- package/lib/services/agent/extendChain.js +177 -0
- package/lib/services/agent/index.d.ts +11 -1
- package/lib/services/agent/index.d.ts.map +1 -1
- package/lib/services/agent/index.js +11 -1
- package/lib/services/agent/llmCaller.d.ts +7 -8
- package/lib/services/agent/llmCaller.d.ts.map +1 -1
- package/lib/services/agent/llmCallerAnthropic.d.ts +44 -31
- package/lib/services/agent/llmCallerAnthropic.d.ts.map +1 -1
- package/lib/services/agent/llmCallerAnthropic.js +135 -60
- package/lib/services/agent/llmCallerFactory.d.ts +34 -0
- package/lib/services/agent/llmCallerFactory.d.ts.map +1 -0
- package/lib/services/agent/llmCallerFactory.js +31 -0
- package/lib/services/agent/llmCallerGemini.d.ts +62 -0
- package/lib/services/agent/llmCallerGemini.d.ts.map +1 -0
- package/lib/services/agent/llmCallerGemini.js +235 -0
- package/lib/services/agent/llmCallerOpenai.d.ts +56 -0
- package/lib/services/agent/llmCallerOpenai.d.ts.map +1 -0
- package/lib/services/agent/llmCallerOpenai.js +230 -0
- package/lib/services/agent/llmCallerRegistry.d.ts.map +1 -1
- package/lib/services/agent/llmCallerRegistry.js +7 -7
- package/lib/services/agent/llmCallerRouting.d.ts +63 -0
- package/lib/services/agent/llmCallerRouting.d.ts.map +1 -0
- package/lib/services/agent/llmCallerRouting.js +124 -0
- package/lib/services/agent/llmModelRegistry.d.ts +59 -0
- package/lib/services/agent/llmModelRegistry.d.ts.map +1 -0
- package/lib/services/agent/llmModelRegistry.js +168 -0
- package/lib/services/agent/llmRetry.d.ts +57 -0
- package/lib/services/agent/llmRetry.d.ts.map +1 -0
- package/lib/services/agent/llmRetry.js +102 -0
- package/lib/services/agent/modelRouter.d.ts +3 -3
- package/lib/services/agent/modelRouter.d.ts.map +1 -1
- package/lib/services/agent/modelRouter.js +27 -13
- package/lib/services/agent/planMutations.d.ts +54 -1
- package/lib/services/agent/planMutations.d.ts.map +1 -1
- package/lib/services/agent/planMutations.js +78 -0
- package/lib/services/agent/planner/Planner.d.ts +0 -17
- package/lib/services/agent/planner/Planner.d.ts.map +1 -1
- package/lib/services/agent/planner/Planner.js +67 -303
- package/lib/services/agent/planner/overlayRegen.d.ts +38 -0
- package/lib/services/agent/planner/overlayRegen.d.ts.map +1 -0
- package/lib/services/agent/planner/overlayRegen.js +145 -0
- package/lib/services/agent/planner/plannerMessages.d.ts +34 -0
- package/lib/services/agent/planner/plannerMessages.d.ts.map +1 -0
- package/lib/services/agent/planner/plannerMessages.js +185 -0
- package/lib/services/agent/planner/promptSections.d.ts +12 -0
- package/lib/services/agent/planner/promptSections.d.ts.map +1 -1
- package/lib/services/agent/planner/promptSections.js +57 -0
- package/lib/services/agent/planner/scriptFirstPlanner.d.ts +35 -0
- package/lib/services/agent/planner/scriptFirstPlanner.d.ts.map +1 -0
- package/lib/services/agent/planner/scriptFirstPlanner.js +140 -0
- package/lib/services/agent/planner/structuralRules.d.ts +10 -0
- package/lib/services/agent/planner/structuralRules.d.ts.map +1 -1
- package/lib/services/agent/planner/structuralRules.js +92 -9
- package/lib/services/agent/planner/validators.d.ts +18 -0
- package/lib/services/agent/planner/validators.d.ts.map +1 -1
- package/lib/services/agent/planner/validators.js +97 -0
- package/lib/services/agent/planner.d.ts +2 -1
- package/lib/services/agent/planner.d.ts.map +1 -1
- package/lib/services/agent/planner.js +5 -1
- package/lib/services/agent/priorProject.d.ts +26 -0
- package/lib/services/agent/priorProject.d.ts.map +1 -0
- package/lib/services/agent/priorProject.js +51 -0
- package/lib/services/agent/providerFallback/chains.d.ts.map +1 -1
- package/lib/services/agent/providerFallback/chains.js +27 -15
- package/lib/services/agent/repurpose/index.d.ts +3 -0
- package/lib/services/agent/repurpose/index.d.ts.map +1 -0
- package/lib/services/agent/repurpose/index.js +18 -0
- package/lib/services/agent/repurpose/repurpose.schema.d.ts +132 -0
- package/lib/services/agent/repurpose/repurpose.schema.d.ts.map +1 -0
- package/lib/services/agent/repurpose/repurpose.schema.js +144 -0
- package/lib/services/agent/repurpose/shortsPicker.d.ts +25 -0
- package/lib/services/agent/repurpose/shortsPicker.d.ts.map +1 -0
- package/lib/services/agent/repurpose/shortsPicker.js +218 -0
- package/lib/services/agent/runHelpers.d.ts +21 -2
- package/lib/services/agent/runHelpers.d.ts.map +1 -1
- package/lib/services/agent/runHelpers.js +71 -2
- package/lib/services/agent/tools/animateImage.tool.d.ts +1 -0
- package/lib/services/agent/tools/animateImage.tool.d.ts.map +1 -1
- package/lib/services/agent/tools/animateImage.tool.js +12 -0
- package/lib/services/agent/tools/chapterOutline.tool.d.ts +42 -0
- package/lib/services/agent/tools/chapterOutline.tool.d.ts.map +1 -0
- package/lib/services/agent/tools/chapterOutline.tool.js +115 -0
- package/lib/services/agent/tools/composeScene.tool.d.ts +63 -0
- package/lib/services/agent/tools/composeScene.tool.d.ts.map +1 -1
- package/lib/services/agent/tools/estimateCost.tool.d.ts +27 -0
- package/lib/services/agent/tools/estimateCost.tool.d.ts.map +1 -1
- package/lib/services/agent/tools/estimateCost.tool.js +55 -7
- package/lib/services/agent/tools/extendVideo.tool.d.ts +26 -0
- package/lib/services/agent/tools/extendVideo.tool.d.ts.map +1 -0
- package/lib/services/agent/tools/extendVideo.tool.js +149 -0
- package/lib/services/agent/tools/generateScript.tool.d.ts +184 -0
- package/lib/services/agent/tools/generateScript.tool.d.ts.map +1 -0
- package/lib/services/agent/tools/generateScript.tool.js +123 -0
- package/lib/services/agent/tools/generateVideo.tool.d.ts +1 -0
- package/lib/services/agent/tools/generateVideo.tool.d.ts.map +1 -1
- package/lib/services/agent/tools/generateVideo.tool.js +20 -1
- package/lib/services/agent/tools/index.d.ts +4 -0
- package/lib/services/agent/tools/index.d.ts.map +1 -1
- package/lib/services/agent/tools/index.js +4 -0
- package/lib/services/agent/tools/matchBrollToScript.tool.d.ts +50 -0
- package/lib/services/agent/tools/matchBrollToScript.tool.d.ts.map +1 -0
- package/lib/services/agent/tools/matchBrollToScript.tool.js +139 -0
- package/lib/services/agent/tools/planVideo.tool.d.ts +56 -0
- package/lib/services/agent/tools/planVideo.tool.d.ts.map +1 -1
- package/lib/services/agent/tools/planVideo.tool.js +3 -3
- package/lib/services/agent/tools/render.tool.d.ts +21 -0
- package/lib/services/agent/tools/render.tool.d.ts.map +1 -1
- package/lib/services/aiGen/aiGenFactory.service.d.ts.map +1 -1
- package/lib/services/aiGen/aiGenFactory.service.js +18 -3
- package/lib/services/aiGen/providers/anthropic/anthropic.service.d.ts +26 -0
- package/lib/services/aiGen/providers/anthropic/anthropic.service.d.ts.map +1 -0
- package/lib/services/aiGen/providers/anthropic/anthropic.service.js +95 -0
- package/lib/services/aiGen/providers/google/google.service.d.ts +24 -1
- package/lib/services/aiGen/providers/google/google.service.d.ts.map +1 -1
- package/lib/services/aiGen/providers/google/google.service.js +87 -243
- package/lib/services/aiGen/providers/google/googleErrors.d.ts +13 -0
- package/lib/services/aiGen/providers/google/googleErrors.d.ts.map +1 -0
- package/lib/services/aiGen/providers/google/googleErrors.js +102 -0
- package/lib/services/aiGen/providers/google/googleFetch.d.ts +8 -0
- package/lib/services/aiGen/providers/google/googleFetch.d.ts.map +1 -0
- package/lib/services/aiGen/providers/google/googleFetch.js +96 -0
- package/lib/services/aiGen/providers/google/googleMusic.d.ts +15 -0
- package/lib/services/aiGen/providers/google/googleMusic.d.ts.map +1 -0
- package/lib/services/aiGen/providers/google/googleMusic.js +77 -0
- package/lib/services/aiGen/providers/kling/kling.service.d.ts +7 -3
- package/lib/services/aiGen/providers/kling/kling.service.d.ts.map +1 -1
- package/lib/services/aiGen/providers/kling/kling.service.js +23 -367
- package/lib/services/aiGen/providers/kling/klingCredits.d.ts +9 -0
- package/lib/services/aiGen/providers/kling/klingCredits.d.ts.map +1 -0
- package/lib/services/aiGen/providers/kling/klingCredits.js +63 -0
- package/lib/services/aiGen/providers/kling/klingRequests.d.ts +32 -0
- package/lib/services/aiGen/providers/kling/klingRequests.d.ts.map +1 -0
- package/lib/services/aiGen/providers/kling/klingRequests.js +194 -0
- package/lib/services/aiGen/providers/kling/klingStatus.d.ts +16 -0
- package/lib/services/aiGen/providers/kling/klingStatus.d.ts.map +1 -0
- package/lib/services/aiGen/providers/kling/klingStatus.js +173 -0
- package/lib/services/bullmq.service.d.ts +61 -0
- package/lib/services/bullmq.service.d.ts.map +1 -1
- package/lib/services/bullmq.service.js +101 -1
- package/lib/services/crypto/index.d.ts +2 -0
- package/lib/services/crypto/index.d.ts.map +1 -0
- package/lib/services/crypto/index.js +17 -0
- package/lib/services/crypto/tokenVault.d.ts +47 -0
- package/lib/services/crypto/tokenVault.d.ts.map +1 -0
- package/lib/services/crypto/tokenVault.js +179 -0
- package/lib/services/editor/captionStyleHint.d.ts +3 -0
- package/lib/services/editor/captionStyleHint.d.ts.map +1 -0
- package/lib/services/editor/captionStyleHint.js +112 -0
- package/lib/services/editor/planToProject.d.ts +7 -66
- package/lib/services/editor/planToProject.d.ts.map +1 -1
- package/lib/services/editor/planToProject.helpers.d.ts +40 -0
- package/lib/services/editor/planToProject.helpers.d.ts.map +1 -0
- package/lib/services/editor/planToProject.helpers.js +177 -0
- package/lib/services/editor/planToProject.js +197 -180
- package/lib/services/editor/planToProject.types.d.ts +94 -0
- package/lib/services/editor/planToProject.types.d.ts.map +1 -0
- package/lib/services/editor/planToProject.types.js +2 -0
- package/lib/services/firestore.service.d.ts +5 -0
- package/lib/services/firestore.service.d.ts.map +1 -1
- package/lib/services/firestore.service.js +13 -0
- package/lib/services/index.d.ts +12 -0
- package/lib/services/index.d.ts.map +1 -1
- package/lib/services/index.js +12 -0
- package/lib/services/promptEnhancer/index.d.ts +18 -0
- package/lib/services/promptEnhancer/index.d.ts.map +1 -0
- package/lib/services/promptEnhancer/index.js +33 -0
- package/lib/services/promptEnhancer/models.d.ts +54 -0
- package/lib/services/promptEnhancer/models.d.ts.map +1 -0
- package/lib/services/promptEnhancer/models.js +37 -0
- package/lib/services/promptEnhancer/profiles/agent.profile.d.ts +14 -0
- package/lib/services/promptEnhancer/profiles/agent.profile.d.ts.map +1 -0
- package/lib/services/promptEnhancer/profiles/agent.profile.js +40 -0
- package/lib/services/promptEnhancer/profiles/avatar.profile.d.ts +13 -0
- package/lib/services/promptEnhancer/profiles/avatar.profile.d.ts.map +1 -0
- package/lib/services/promptEnhancer/profiles/avatar.profile.js +40 -0
- package/lib/services/promptEnhancer/profiles/base.d.ts +28 -0
- package/lib/services/promptEnhancer/profiles/base.d.ts.map +1 -0
- package/lib/services/promptEnhancer/profiles/base.js +35 -0
- package/lib/services/promptEnhancer/profiles/image.profile.d.ts +11 -0
- package/lib/services/promptEnhancer/profiles/image.profile.d.ts.map +1 -0
- package/lib/services/promptEnhancer/profiles/image.profile.js +42 -0
- package/lib/services/promptEnhancer/profiles/index.d.ts +12 -0
- package/lib/services/promptEnhancer/profiles/index.d.ts.map +1 -0
- package/lib/services/promptEnhancer/profiles/index.js +33 -0
- package/lib/services/promptEnhancer/profiles/video.profile.d.ts +15 -0
- package/lib/services/promptEnhancer/profiles/video.profile.d.ts.map +1 -0
- package/lib/services/promptEnhancer/profiles/video.profile.js +81 -0
- package/lib/services/promptEnhancer/promptEnhancer.service.d.ts +45 -0
- package/lib/services/promptEnhancer/promptEnhancer.service.d.ts.map +1 -0
- package/lib/services/promptEnhancer/promptEnhancer.service.js +157 -0
- package/lib/services/promptEnhancer/schema.d.ts +19 -0
- package/lib/services/promptEnhancer/schema.d.ts.map +1 -0
- package/lib/services/promptEnhancer/schema.js +43 -0
- package/lib/services/promptEnhancer/types.d.ts +112 -0
- package/lib/services/promptEnhancer/types.d.ts.map +1 -0
- package/lib/services/promptEnhancer/types.js +2 -0
- package/lib/services/socialAI/captionGen.d.ts +81 -0
- package/lib/services/socialAI/captionGen.d.ts.map +1 -0
- package/lib/services/socialAI/captionGen.js +206 -0
- package/lib/services/socialAI/hookScore.d.ts +85 -0
- package/lib/services/socialAI/hookScore.d.ts.map +1 -0
- package/lib/services/socialAI/hookScore.js +170 -0
- package/lib/services/socialAI/index.d.ts +3 -0
- package/lib/services/socialAI/index.d.ts.map +1 -0
- package/lib/services/socialAI/index.js +18 -0
- package/lib/services/socialAccounts/index.d.ts +2 -0
- package/lib/services/socialAccounts/index.d.ts.map +1 -0
- package/lib/services/socialAccounts/index.js +17 -0
- package/lib/services/socialAccounts/socialAccountService.d.ts +25 -0
- package/lib/services/socialAccounts/socialAccountService.d.ts.map +1 -0
- package/lib/services/socialAccounts/socialAccountService.js +105 -0
- package/lib/services/socialEngage/factory.d.ts +7 -0
- package/lib/services/socialEngage/factory.d.ts.map +1 -0
- package/lib/services/socialEngage/factory.js +25 -0
- package/lib/services/socialEngage/index.d.ts +6 -0
- package/lib/services/socialEngage/index.d.ts.map +1 -0
- package/lib/services/socialEngage/index.js +21 -0
- package/lib/services/socialEngage/meta.engage.d.ts +17 -0
- package/lib/services/socialEngage/meta.engage.d.ts.map +1 -0
- package/lib/services/socialEngage/meta.engage.js +128 -0
- package/lib/services/socialEngage/metaWebhook.d.ts +50 -0
- package/lib/services/socialEngage/metaWebhook.d.ts.map +1 -0
- package/lib/services/socialEngage/metaWebhook.js +93 -0
- package/lib/services/socialEngage/types.d.ts +72 -0
- package/lib/services/socialEngage/types.d.ts.map +1 -0
- package/lib/services/socialEngage/types.js +10 -0
- package/lib/services/socialEngage/youtube.engage.d.ts +9 -0
- package/lib/services/socialEngage/youtube.engage.d.ts.map +1 -0
- package/lib/services/socialEngage/youtube.engage.js +87 -0
- package/lib/services/socialFormat/aspectGeometry.d.ts +74 -0
- package/lib/services/socialFormat/aspectGeometry.d.ts.map +1 -0
- package/lib/services/socialFormat/aspectGeometry.js +135 -0
- package/lib/services/socialFormat/index.d.ts +2 -0
- package/lib/services/socialFormat/index.d.ts.map +1 -0
- package/lib/services/socialFormat/index.js +19 -0
- package/lib/services/socialInsights/index.d.ts +3 -0
- package/lib/services/socialInsights/index.d.ts.map +1 -0
- package/lib/services/socialInsights/index.js +18 -0
- package/lib/services/socialInsights/recommendations.d.ts +131 -0
- package/lib/services/socialInsights/recommendations.d.ts.map +1 -0
- package/lib/services/socialInsights/recommendations.js +277 -0
- package/lib/services/socialInsights/timeBuckets.d.ts +35 -0
- package/lib/services/socialInsights/timeBuckets.d.ts.map +1 -0
- package/lib/services/socialInsights/timeBuckets.js +78 -0
- package/lib/services/socialMetrics/factory.d.ts +5 -0
- package/lib/services/socialMetrics/factory.d.ts.map +1 -0
- package/lib/services/socialMetrics/factory.js +24 -0
- package/lib/services/socialMetrics/index.d.ts +6 -0
- package/lib/services/socialMetrics/index.d.ts.map +1 -0
- package/lib/services/socialMetrics/index.js +21 -0
- package/lib/services/socialMetrics/meta.metrics.d.ts +22 -0
- package/lib/services/socialMetrics/meta.metrics.d.ts.map +1 -0
- package/lib/services/socialMetrics/meta.metrics.js +137 -0
- package/lib/services/socialMetrics/tiktok.metrics.d.ts +8 -0
- package/lib/services/socialMetrics/tiktok.metrics.d.ts.map +1 -0
- package/lib/services/socialMetrics/tiktok.metrics.js +43 -0
- package/lib/services/socialMetrics/types.d.ts +54 -0
- package/lib/services/socialMetrics/types.d.ts.map +1 -0
- package/lib/services/socialMetrics/types.js +2 -0
- package/lib/services/socialMetrics/youtube.metrics.d.ts +8 -0
- package/lib/services/socialMetrics/youtube.metrics.d.ts.map +1 -0
- package/lib/services/socialMetrics/youtube.metrics.js +43 -0
- package/lib/services/socialOAuth/factory.d.ts +7 -0
- package/lib/services/socialOAuth/factory.d.ts.map +1 -0
- package/lib/services/socialOAuth/factory.js +42 -0
- package/lib/services/socialOAuth/index.d.ts +11 -0
- package/lib/services/socialOAuth/index.d.ts.map +1 -0
- package/lib/services/socialOAuth/index.js +26 -0
- package/lib/services/socialOAuth/linkedin.oauth.d.ts +14 -0
- package/lib/services/socialOAuth/linkedin.oauth.d.ts.map +1 -0
- package/lib/services/socialOAuth/linkedin.oauth.js +127 -0
- package/lib/services/socialOAuth/meta.oauth.d.ts +31 -0
- package/lib/services/socialOAuth/meta.oauth.d.ts.map +1 -0
- package/lib/services/socialOAuth/meta.oauth.js +214 -0
- package/lib/services/socialOAuth/oauthState.d.ts +14 -0
- package/lib/services/socialOAuth/oauthState.d.ts.map +1 -0
- package/lib/services/socialOAuth/oauthState.js +66 -0
- package/lib/services/socialOAuth/pinterest.oauth.d.ts +15 -0
- package/lib/services/socialOAuth/pinterest.oauth.d.ts.map +1 -0
- package/lib/services/socialOAuth/pinterest.oauth.js +126 -0
- package/lib/services/socialOAuth/threads.oauth.d.ts +14 -0
- package/lib/services/socialOAuth/threads.oauth.d.ts.map +1 -0
- package/lib/services/socialOAuth/threads.oauth.js +129 -0
- package/lib/services/socialOAuth/tiktok.oauth.d.ts +15 -0
- package/lib/services/socialOAuth/tiktok.oauth.d.ts.map +1 -0
- package/lib/services/socialOAuth/tiktok.oauth.js +151 -0
- package/lib/services/socialOAuth/types.d.ts +67 -0
- package/lib/services/socialOAuth/types.d.ts.map +1 -0
- package/lib/services/socialOAuth/types.js +2 -0
- package/lib/services/socialOAuth/x.oauth.d.ts +17 -0
- package/lib/services/socialOAuth/x.oauth.d.ts.map +1 -0
- package/lib/services/socialOAuth/x.oauth.js +134 -0
- package/lib/services/socialOAuth/youtube.oauth.d.ts +15 -0
- package/lib/services/socialOAuth/youtube.oauth.d.ts.map +1 -0
- package/lib/services/socialOAuth/youtube.oauth.js +156 -0
- package/lib/services/socialPublish/factory.d.ts +5 -0
- package/lib/services/socialPublish/factory.d.ts.map +1 -0
- package/lib/services/socialPublish/factory.js +32 -0
- package/lib/services/socialPublish/index.d.ts +10 -0
- package/lib/services/socialPublish/index.d.ts.map +1 -0
- package/lib/services/socialPublish/index.js +25 -0
- package/lib/services/socialPublish/linkedin.publish.d.ts +9 -0
- package/lib/services/socialPublish/linkedin.publish.d.ts.map +1 -0
- package/lib/services/socialPublish/linkedin.publish.js +143 -0
- package/lib/services/socialPublish/meta.publish.d.ts +28 -0
- package/lib/services/socialPublish/meta.publish.d.ts.map +1 -0
- package/lib/services/socialPublish/meta.publish.js +149 -0
- package/lib/services/socialPublish/pinterest.publish.d.ts +13 -0
- package/lib/services/socialPublish/pinterest.publish.d.ts.map +1 -0
- package/lib/services/socialPublish/pinterest.publish.js +130 -0
- package/lib/services/socialPublish/threads.publish.d.ts +12 -0
- package/lib/services/socialPublish/threads.publish.d.ts.map +1 -0
- package/lib/services/socialPublish/threads.publish.js +96 -0
- package/lib/services/socialPublish/tiktok.publish.d.ts +13 -0
- package/lib/services/socialPublish/tiktok.publish.d.ts.map +1 -0
- package/lib/services/socialPublish/tiktok.publish.js +118 -0
- package/lib/services/socialPublish/types.d.ts +47 -0
- package/lib/services/socialPublish/types.d.ts.map +1 -0
- package/lib/services/socialPublish/types.js +2 -0
- package/lib/services/socialPublish/x.publish.d.ts +12 -0
- package/lib/services/socialPublish/x.publish.d.ts.map +1 -0
- package/lib/services/socialPublish/x.publish.js +147 -0
- package/lib/services/socialPublish/youtube.publish.d.ts +9 -0
- package/lib/services/socialPublish/youtube.publish.d.ts.map +1 -0
- package/lib/services/socialPublish/youtube.publish.js +107 -0
- package/lib/services/stock/index.d.ts +2 -0
- package/lib/services/stock/index.d.ts.map +1 -0
- package/lib/services/stock/index.js +17 -0
- package/lib/services/stock/realPersonSafety.d.ts +99 -0
- package/lib/services/stock/realPersonSafety.d.ts.map +1 -0
- package/lib/services/stock/realPersonSafety.js +248 -0
- package/lib/utils/index.d.ts +1 -0
- package/lib/utils/index.d.ts.map +1 -1
- package/lib/utils/index.js +1 -0
- package/lib/utils/renderTier.d.ts +26 -0
- package/lib/utils/renderTier.d.ts.map +1 -0
- package/lib/utils/renderTier.js +34 -0
- package/package.json +1 -1
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* X8 Social Suite — B2 auto-format geometry.
|
|
4
|
+
*
|
|
5
|
+
* Pure, dependency-free math for re-framing an already-rendered video into a
|
|
6
|
+
* platform's recommended aspect. Lives in `shared` (not the node app) so it is
|
|
7
|
+
* unit-testable and reused by both the publish worker and any future preview UI.
|
|
8
|
+
*
|
|
9
|
+
* The plan's intent (SOCIAL_SUITE_PLAN.md §4 B2): *don't re-encode blindly* — but
|
|
10
|
+
* for plain generated videos we only have the stitched MP4 (no Project JSON), so
|
|
11
|
+
* the reframe is an ffmpeg scale+crop/pad transform here rather than a Remotion
|
|
12
|
+
* re-render. Agent projects that DO carry Project JSON can later route through
|
|
13
|
+
* the Remotion path; this module only computes target dimensions + the ffmpeg
|
|
14
|
+
* filter, so both callers share the same geometry.
|
|
15
|
+
*
|
|
16
|
+
* Two modes:
|
|
17
|
+
* - "cover" (default) — scale to fill the target frame, then center-crop the
|
|
18
|
+
* overflow. No bars; loses edge content. This is what Reels/Shorts
|
|
19
|
+
* auto-format expects. (Smart/face-aware crop is deferred — v1 is
|
|
20
|
+
* a center crop.)
|
|
21
|
+
* - "contain" — scale to fit inside the target frame, then pad (letterbox /
|
|
22
|
+
* pillarbox) with a solid colour. Preserves all content; adds bars.
|
|
23
|
+
*/
|
|
24
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
25
|
+
exports.SUPPORTED_ASPECTS = void 0;
|
|
26
|
+
exports.parseAspect = parseAspect;
|
|
27
|
+
exports.aspectRatio = aspectRatio;
|
|
28
|
+
exports.targetSizeFor = targetSizeFor;
|
|
29
|
+
exports.needsReframe = needsReframe;
|
|
30
|
+
exports.buildReframeFilter = buildReframeFilter;
|
|
31
|
+
exports.resolveReframe = resolveReframe;
|
|
32
|
+
/** Aspects the composer can request (mirrors AspectSchema + a few publish-only ratios). */
|
|
33
|
+
exports.SUPPORTED_ASPECTS = ["9:16", "1:1", "4:5", "16:9", "4:3", "3:4"];
|
|
34
|
+
/**
|
|
35
|
+
* Canonical upload dimensions per aspect — the resolution each platform
|
|
36
|
+
* recommends. Even-numbered (h264 requires even width/height). Portrait/Reels
|
|
37
|
+
* caps the long edge at 1920 (1080×1920); square/landscape use 1080-class.
|
|
38
|
+
*/
|
|
39
|
+
const CANONICAL_SIZE = {
|
|
40
|
+
"9:16": { width: 1080, height: 1920 },
|
|
41
|
+
"16:9": { width: 1920, height: 1080 },
|
|
42
|
+
"1:1": { width: 1080, height: 1080 },
|
|
43
|
+
"4:5": { width: 1080, height: 1350 },
|
|
44
|
+
"4:3": { width: 1440, height: 1080 },
|
|
45
|
+
"3:4": { width: 1080, height: 1440 },
|
|
46
|
+
};
|
|
47
|
+
/** Parse "9:16" → { w: 9, h: 16 }. Throws on malformed input (validated upstream by zod). */
|
|
48
|
+
function parseAspect(aspect) {
|
|
49
|
+
const m = /^(\d{1,2}):(\d{1,2})$/.exec(aspect.trim());
|
|
50
|
+
if (!m)
|
|
51
|
+
throw new Error(`invalid aspect "${aspect}"`);
|
|
52
|
+
const w = Number(m[1]);
|
|
53
|
+
const h = Number(m[2]);
|
|
54
|
+
if (!w || !h)
|
|
55
|
+
throw new Error(`invalid aspect "${aspect}"`);
|
|
56
|
+
return { w, h };
|
|
57
|
+
}
|
|
58
|
+
/** Numeric width/height ratio of an aspect string (e.g. "16:9" → 1.777…). */
|
|
59
|
+
function aspectRatio(aspect) {
|
|
60
|
+
const { w, h } = parseAspect(aspect);
|
|
61
|
+
return w / h;
|
|
62
|
+
}
|
|
63
|
+
/** Round to the nearest even integer (h264 demands even pixel dimensions). */
|
|
64
|
+
function even(n) {
|
|
65
|
+
return Math.max(2, 2 * Math.round(n / 2));
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Target output dimensions for an aspect. Uses the canonical table when known;
|
|
69
|
+
* otherwise derives a ~1080-class box from the parsed ratio so any zod-valid
|
|
70
|
+
* "W:H" still produces sane even dimensions.
|
|
71
|
+
*/
|
|
72
|
+
function targetSizeFor(aspect) {
|
|
73
|
+
const known = CANONICAL_SIZE[aspect];
|
|
74
|
+
if (known)
|
|
75
|
+
return known;
|
|
76
|
+
const { w, h } = parseAspect(aspect);
|
|
77
|
+
// Base the shorter side on 1080 and scale the longer side off the ratio.
|
|
78
|
+
if (w >= h) {
|
|
79
|
+
return { width: even((1080 * w) / h), height: 1080 };
|
|
80
|
+
}
|
|
81
|
+
return { width: 1080, height: even((1080 * h) / w) };
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Whether a source of (srcW × srcH) needs re-framing to hit `targetAspect`.
|
|
85
|
+
* Returns false when the source ratio already matches within `tolerance`
|
|
86
|
+
* (default 1%) — the publish worker then skips the render and publishes the
|
|
87
|
+
* source as-is (no wasted transcode, no quality loss).
|
|
88
|
+
*/
|
|
89
|
+
function needsReframe(srcWidth, srcHeight, targetAspect, tolerance = 0.01) {
|
|
90
|
+
if (!srcWidth || !srcHeight)
|
|
91
|
+
return true;
|
|
92
|
+
const src = srcWidth / srcHeight;
|
|
93
|
+
const target = aspectRatio(targetAspect);
|
|
94
|
+
return Math.abs(src - target) / target > tolerance;
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Build the ffmpeg `-vf` filter string that reframes a source of (srcW × srcH)
|
|
98
|
+
* into (dstW × dstH) using the chosen mode. SAR is reset to 1 so non-square
|
|
99
|
+
* source pixels don't skew the output.
|
|
100
|
+
*
|
|
101
|
+
* cover: scale to the smallest size that fully covers the target (increase),
|
|
102
|
+
* then center-crop to exact target dimensions.
|
|
103
|
+
* contain: scale to the largest size that fits inside (decrease), then pad the
|
|
104
|
+
* remainder centred with `padColor`.
|
|
105
|
+
*/
|
|
106
|
+
function buildReframeFilter(dstWidth, dstHeight, mode = "cover", padColor = "black") {
|
|
107
|
+
if (mode === "contain") {
|
|
108
|
+
return [
|
|
109
|
+
`scale=${dstWidth}:${dstHeight}:force_original_aspect_ratio=decrease`,
|
|
110
|
+
`pad=${dstWidth}:${dstHeight}:(ow-iw)/2:(oh-ih)/2:color=${padColor}`,
|
|
111
|
+
`setsar=1`,
|
|
112
|
+
].join(",");
|
|
113
|
+
}
|
|
114
|
+
return [
|
|
115
|
+
`scale=${dstWidth}:${dstHeight}:force_original_aspect_ratio=increase`,
|
|
116
|
+
`crop=${dstWidth}:${dstHeight}`,
|
|
117
|
+
`setsar=1`,
|
|
118
|
+
].join(",");
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* One-shot geometry resolution for a reframe job: given the probed source
|
|
122
|
+
* dimensions and a requested aspect, return the target dimensions, whether a
|
|
123
|
+
* reframe is actually required, and the ready-to-use ffmpeg filter. The worker
|
|
124
|
+
* calls this and either skips (when `reframe` is false) or runs ffmpeg with
|
|
125
|
+
* `filter`.
|
|
126
|
+
*/
|
|
127
|
+
function resolveReframe(srcWidth, srcHeight, targetAspect, mode = "cover") {
|
|
128
|
+
const { width, height } = targetSizeFor(targetAspect);
|
|
129
|
+
return {
|
|
130
|
+
width,
|
|
131
|
+
height,
|
|
132
|
+
reframe: needsReframe(srcWidth, srcHeight, targetAspect),
|
|
133
|
+
filter: buildReframeFilter(width, height, mode),
|
|
134
|
+
};
|
|
135
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/services/socialFormat/index.ts"],"names":[],"mappings":"AAEA,cAAc,kBAAkB,CAAC"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
// X8 Social Suite — B2 auto-format. Pure geometry shared by the publish worker
|
|
18
|
+
// (ffmpeg reframe) and any future per-platform preview. See aspectGeometry.ts.
|
|
19
|
+
__exportStar(require("./aspectGeometry"), exports);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/services/socialInsights/index.ts"],"names":[],"mappings":"AAAA,cAAc,eAAe,CAAC;AAC9B,cAAc,mBAAmB,CAAC"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
__exportStar(require("./timeBuckets"), exports);
|
|
18
|
+
__exportStar(require("./recommendations"), exports);
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import { ESocialProvider } from "../../globals/types";
|
|
2
|
+
/**
|
|
3
|
+
* X8 Social Suite — Phase 6 C5 (growth insights / recommendations) + E5
|
|
4
|
+
* (content ideas). Two halves, deliberately split:
|
|
5
|
+
*
|
|
6
|
+
* 1. `deriveInsightSignals()` — PURE, deterministic stats over the user's OWN
|
|
7
|
+
* posts + snapshots. Every number the user sees is computed here, never by
|
|
8
|
+
* the model. This is the anti-hallucination guarantee: the LLM is handed
|
|
9
|
+
* finished facts ("Instagram averages 2.1× your YouTube views over 6
|
|
10
|
+
* posts") and only phrases them — it can't invent a stat.
|
|
11
|
+
* 2. `generateRecommendationsCore(llm, …)` — turns those grounded facts into a
|
|
12
|
+
* short weekly-digest summary, concrete recommendations (C5), and next-post
|
|
13
|
+
* content ideas (E5). Defensive parse; degrades to the deterministic facts
|
|
14
|
+
* when the model misbehaves, so the panel always renders something true.
|
|
15
|
+
*
|
|
16
|
+
* Same `*Core(llm,…)` convention as captionGen/hookScore — unit-/eval-testable.
|
|
17
|
+
*/
|
|
18
|
+
/** A published post reduced to the fields insights need (no Firestore types). */
|
|
19
|
+
export interface IInsightPost {
|
|
20
|
+
postId: string;
|
|
21
|
+
/** The primary provider this post went to (first target's provider). */
|
|
22
|
+
provider: ESocialProvider;
|
|
23
|
+
/** Publish time in unix ms (or createdAt fallback). */
|
|
24
|
+
publishedAtMs: number;
|
|
25
|
+
/** Caption (used only for the top-post snippet — never invented). */
|
|
26
|
+
caption?: string;
|
|
27
|
+
/** Source video length in seconds, when cheaply known (drives the length cut). */
|
|
28
|
+
durationSec?: number;
|
|
29
|
+
}
|
|
30
|
+
/** A latest-per-surface metric row reduced to what insights need. */
|
|
31
|
+
export interface IInsightMetricRow {
|
|
32
|
+
socialPostId: string;
|
|
33
|
+
provider: ESocialProvider;
|
|
34
|
+
views: number;
|
|
35
|
+
/** likes + comments + shares + saves (precomputed engagement count). */
|
|
36
|
+
engagement: number;
|
|
37
|
+
saves: number;
|
|
38
|
+
shares: number;
|
|
39
|
+
comments: number;
|
|
40
|
+
likes: number;
|
|
41
|
+
}
|
|
42
|
+
export interface IInsightSignalsInput {
|
|
43
|
+
posts: IInsightPost[];
|
|
44
|
+
metrics: IInsightMetricRow[];
|
|
45
|
+
/** IANA zone for the timing signal; defaults to UTC. */
|
|
46
|
+
timeZone?: string;
|
|
47
|
+
/** "now" in unix ms — injected so the cadence math is testable/deterministic. */
|
|
48
|
+
nowMs: number;
|
|
49
|
+
}
|
|
50
|
+
export interface IProviderPerformance {
|
|
51
|
+
provider: ESocialProvider;
|
|
52
|
+
posts: number;
|
|
53
|
+
avgViews: number;
|
|
54
|
+
/** Engagement rate 0..1 (engagement / views), 0 when no views. */
|
|
55
|
+
engagementRate: number;
|
|
56
|
+
}
|
|
57
|
+
/** The structured, fully-derived signal set (every value computed, not guessed). */
|
|
58
|
+
export interface IInsightSignals {
|
|
59
|
+
/** Posts that have at least one metric snapshot. */
|
|
60
|
+
measuredPosts: number;
|
|
61
|
+
overallAvgViews: number;
|
|
62
|
+
overallEngagementRate: number;
|
|
63
|
+
/** Per-provider performance, sorted by avgViews desc. */
|
|
64
|
+
providers: IProviderPerformance[];
|
|
65
|
+
/** Provider with the highest avgViews (min 2 posts), if any. */
|
|
66
|
+
topProvider: IProviderPerformance | null;
|
|
67
|
+
/** Best (weekday,hour) by avgViews in `timeZone` (min 2 posts), if any. */
|
|
68
|
+
bestTime: {
|
|
69
|
+
weekday: number;
|
|
70
|
+
hour: number;
|
|
71
|
+
avgViews: number;
|
|
72
|
+
} | null;
|
|
73
|
+
/** Dominant interaction type across all engagement, if any stands out. */
|
|
74
|
+
dominantInteraction: "saves" | "shares" | "comments" | "likes" | null;
|
|
75
|
+
/** Posts published in the trailing 7 / 30 days. */
|
|
76
|
+
postsLast7: number;
|
|
77
|
+
postsLast30: number;
|
|
78
|
+
/** Top post by views (id + caption snippet + views), if any. */
|
|
79
|
+
topPost: {
|
|
80
|
+
postId: string;
|
|
81
|
+
snippet: string;
|
|
82
|
+
views: number;
|
|
83
|
+
} | null;
|
|
84
|
+
/** Length cut: avg views for short(<30s)/mid(30-60s)/long(>60s), each ≥2 posts. */
|
|
85
|
+
lengthBuckets: {
|
|
86
|
+
bucket: "short" | "mid" | "long";
|
|
87
|
+
posts: number;
|
|
88
|
+
avgViews: number;
|
|
89
|
+
}[];
|
|
90
|
+
/** Human-readable grounded facts fed to the LLM (and a deterministic fallback). */
|
|
91
|
+
facts: string[];
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Derive the deterministic signal set. Pure — no I/O, no `Date.now()` (the
|
|
95
|
+
* caller injects `nowMs`). Cold-start safe: returns zeros/nulls + empty facts
|
|
96
|
+
* when there's nothing measured yet.
|
|
97
|
+
*/
|
|
98
|
+
export declare function deriveInsightSignals(input: IInsightSignalsInput): IInsightSignals;
|
|
99
|
+
/** The LLM-phrased digest. */
|
|
100
|
+
export interface IGrowthRecommendations {
|
|
101
|
+
/** 1-2 sentence weekly-digest summary. */
|
|
102
|
+
summary: string;
|
|
103
|
+
/** Concrete, grounded recommendations (C5). */
|
|
104
|
+
recommendations: string[];
|
|
105
|
+
/** Next-post content ideas grounded in what's working (E5). */
|
|
106
|
+
contentIdeas: string[];
|
|
107
|
+
}
|
|
108
|
+
export type RecommendationsLlm = (messages: Array<{
|
|
109
|
+
role: "system" | "user";
|
|
110
|
+
content: string;
|
|
111
|
+
}>) => Promise<string>;
|
|
112
|
+
/** Build the chat prompt from the GROUNDED facts. Pure — no I/O. */
|
|
113
|
+
export declare function buildRecommendationsPrompt(signals: IInsightSignals, language?: string): Array<{
|
|
114
|
+
role: "system" | "user";
|
|
115
|
+
content: string;
|
|
116
|
+
}>;
|
|
117
|
+
/**
|
|
118
|
+
* Parse the model's JSON into recommendations. Defensive: on malformed JSON it
|
|
119
|
+
* falls back to the deterministic facts as recommendations, so the panel always
|
|
120
|
+
* shows something TRUE (the facts can't be wrong — we computed them).
|
|
121
|
+
*/
|
|
122
|
+
export declare function parseRecommendations(raw: string, signals: IInsightSignals): IGrowthRecommendations;
|
|
123
|
+
/**
|
|
124
|
+
* Generate growth recommendations with the LLM injected (testable). Phrases the
|
|
125
|
+
* pre-computed grounded signals; never throws on a bad response.
|
|
126
|
+
*/
|
|
127
|
+
export declare function generateRecommendationsCore(llm: RecommendationsLlm, args: {
|
|
128
|
+
signals: IInsightSignals;
|
|
129
|
+
language?: string;
|
|
130
|
+
}): Promise<IGrowthRecommendations>;
|
|
131
|
+
//# sourceMappingURL=recommendations.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"recommendations.d.ts","sourceRoot":"","sources":["../../../src/services/socialInsights/recommendations.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.deriveInsightSignals = deriveInsightSignals;
|
|
4
|
+
exports.buildRecommendationsPrompt = buildRecommendationsPrompt;
|
|
5
|
+
exports.parseRecommendations = parseRecommendations;
|
|
6
|
+
exports.generateRecommendationsCore = generateRecommendationsCore;
|
|
7
|
+
const timeBuckets_1 = require("./timeBuckets");
|
|
8
|
+
const MIN_PROVIDER_SAMPLE = 2;
|
|
9
|
+
const MIN_TIME_SAMPLE = 2;
|
|
10
|
+
const MIN_LENGTH_SAMPLE = 2;
|
|
11
|
+
function avg(nums) {
|
|
12
|
+
if (nums.length === 0)
|
|
13
|
+
return 0;
|
|
14
|
+
return nums.reduce((a, b) => a + b, 0) / nums.length;
|
|
15
|
+
}
|
|
16
|
+
function round1(n) {
|
|
17
|
+
return Math.round(n * 10) / 10;
|
|
18
|
+
}
|
|
19
|
+
/** First ~80 chars of a caption on one line — only the user's own words. */
|
|
20
|
+
function snippet(caption) {
|
|
21
|
+
if (!caption)
|
|
22
|
+
return "";
|
|
23
|
+
const one = caption.replace(/\s+/g, " ").trim();
|
|
24
|
+
return one.length > 80 ? `${one.slice(0, 80)}…` : one;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Derive the deterministic signal set. Pure — no I/O, no `Date.now()` (the
|
|
28
|
+
* caller injects `nowMs`). Cold-start safe: returns zeros/nulls + empty facts
|
|
29
|
+
* when there's nothing measured yet.
|
|
30
|
+
*/
|
|
31
|
+
function deriveInsightSignals(input) {
|
|
32
|
+
const { posts, metrics, timeZone, nowMs } = input;
|
|
33
|
+
const viewsByPost = new Map();
|
|
34
|
+
const engByPost = new Map();
|
|
35
|
+
for (const m of metrics) {
|
|
36
|
+
viewsByPost.set(m.socialPostId, (viewsByPost.get(m.socialPostId) ?? 0) + m.views);
|
|
37
|
+
engByPost.set(m.socialPostId, (engByPost.get(m.socialPostId) ?? 0) + m.engagement);
|
|
38
|
+
}
|
|
39
|
+
// Only posts we actually have metrics for count toward averages.
|
|
40
|
+
const measured = posts.filter((p) => viewsByPost.has(p.postId));
|
|
41
|
+
const overallAvgViews = round1(avg(measured.map((p) => viewsByPost.get(p.postId) ?? 0)));
|
|
42
|
+
const totalViews = measured.reduce((a, p) => a + (viewsByPost.get(p.postId) ?? 0), 0);
|
|
43
|
+
const totalEng = measured.reduce((a, p) => a + (engByPost.get(p.postId) ?? 0), 0);
|
|
44
|
+
const overallEngagementRate = totalViews > 0 ? round1((totalEng / totalViews) * 1000) / 1000 : 0;
|
|
45
|
+
// Per-provider performance.
|
|
46
|
+
const byProvider = new Map();
|
|
47
|
+
for (const p of measured) {
|
|
48
|
+
const bucket = byProvider.get(p.provider) ?? { views: [], eng: 0, vws: 0 };
|
|
49
|
+
const v = viewsByPost.get(p.postId) ?? 0;
|
|
50
|
+
bucket.views.push(v);
|
|
51
|
+
bucket.vws += v;
|
|
52
|
+
bucket.eng += engByPost.get(p.postId) ?? 0;
|
|
53
|
+
byProvider.set(p.provider, bucket);
|
|
54
|
+
}
|
|
55
|
+
const providers = [...byProvider.entries()]
|
|
56
|
+
.map(([provider, b]) => ({
|
|
57
|
+
provider,
|
|
58
|
+
posts: b.views.length,
|
|
59
|
+
avgViews: round1(avg(b.views)),
|
|
60
|
+
engagementRate: b.vws > 0 ? round1((b.eng / b.vws) * 1000) / 1000 : 0,
|
|
61
|
+
}))
|
|
62
|
+
.sort((a, b) => b.avgViews - a.avgViews);
|
|
63
|
+
const topProvider = providers.find((p) => p.posts >= MIN_PROVIDER_SAMPLE) ?? null;
|
|
64
|
+
// Timing: best (weekday,hour) by avg views in the user's zone.
|
|
65
|
+
const timeGrid = new Map();
|
|
66
|
+
for (const p of measured) {
|
|
67
|
+
const { weekday, hour } = (0, timeBuckets_1.bucketLocal)(p.publishedAtMs, timeZone);
|
|
68
|
+
const key = `${weekday}:${hour}`;
|
|
69
|
+
const cell = timeGrid.get(key) ?? { views: 0, count: 0 };
|
|
70
|
+
cell.views += viewsByPost.get(p.postId) ?? 0;
|
|
71
|
+
cell.count += 1;
|
|
72
|
+
timeGrid.set(key, cell);
|
|
73
|
+
}
|
|
74
|
+
let bestTime = null;
|
|
75
|
+
for (const [key, cell] of timeGrid) {
|
|
76
|
+
if (cell.count < MIN_TIME_SAMPLE)
|
|
77
|
+
continue;
|
|
78
|
+
const a = cell.views / cell.count;
|
|
79
|
+
if (!bestTime || a > bestTime.avgViews) {
|
|
80
|
+
const [wd, hr] = key.split(":").map(Number);
|
|
81
|
+
bestTime = { weekday: wd, hour: hr, avgViews: round1(a) };
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
// Engagement composition — which interaction the audience favours.
|
|
85
|
+
const comp = { saves: 0, shares: 0, comments: 0, likes: 0 };
|
|
86
|
+
for (const m of metrics) {
|
|
87
|
+
comp.saves += m.saves;
|
|
88
|
+
comp.shares += m.shares;
|
|
89
|
+
comp.comments += m.comments;
|
|
90
|
+
comp.likes += m.likes;
|
|
91
|
+
}
|
|
92
|
+
const compTotal = comp.saves + comp.shares + comp.comments + comp.likes;
|
|
93
|
+
let dominantInteraction = null;
|
|
94
|
+
if (compTotal > 0) {
|
|
95
|
+
const entries = Object.entries(comp);
|
|
96
|
+
entries.sort((a, b) => b[1] - a[1]);
|
|
97
|
+
// Only call it "dominant" if it's a real lead (≥40% of all engagement and
|
|
98
|
+
// not a tie with the runner-up) — otherwise it's noise.
|
|
99
|
+
const [topKind, topVal] = entries[0];
|
|
100
|
+
if (topVal / compTotal >= 0.4 && topVal > (entries[1]?.[1] ?? 0)) {
|
|
101
|
+
dominantInteraction = topKind;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
// Cadence.
|
|
105
|
+
const DAY = 24 * 60 * 60 * 1000;
|
|
106
|
+
const postsLast7 = posts.filter((p) => nowMs - p.publishedAtMs <= 7 * DAY).length;
|
|
107
|
+
const postsLast30 = posts.filter((p) => nowMs - p.publishedAtMs <= 30 * DAY).length;
|
|
108
|
+
// Top post by views.
|
|
109
|
+
let topPost = null;
|
|
110
|
+
for (const p of measured) {
|
|
111
|
+
const v = viewsByPost.get(p.postId) ?? 0;
|
|
112
|
+
if (!topPost || v > topPost.views) {
|
|
113
|
+
topPost = { postId: p.postId, snippet: snippet(p.caption), views: v };
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
// Length cut — only when durations are known for enough posts.
|
|
117
|
+
const lenBuckets = { short: [], mid: [], long: [] };
|
|
118
|
+
for (const p of measured) {
|
|
119
|
+
if (typeof p.durationSec !== "number" || p.durationSec <= 0)
|
|
120
|
+
continue;
|
|
121
|
+
const v = viewsByPost.get(p.postId) ?? 0;
|
|
122
|
+
if (p.durationSec < 30)
|
|
123
|
+
lenBuckets.short.push(v);
|
|
124
|
+
else if (p.durationSec <= 60)
|
|
125
|
+
lenBuckets.mid.push(v);
|
|
126
|
+
else
|
|
127
|
+
lenBuckets.long.push(v);
|
|
128
|
+
}
|
|
129
|
+
const lengthBuckets = [
|
|
130
|
+
["short", lenBuckets.short],
|
|
131
|
+
["mid", lenBuckets.mid],
|
|
132
|
+
["long", lenBuckets.long],
|
|
133
|
+
]
|
|
134
|
+
.filter(([, arr]) => arr.length >= MIN_LENGTH_SAMPLE)
|
|
135
|
+
.map(([bucket, arr]) => ({ bucket, posts: arr.length, avgViews: round1(avg(arr)) }));
|
|
136
|
+
// ── Grounded fact strings (this is what the LLM phrases) ────────────────
|
|
137
|
+
const facts = [];
|
|
138
|
+
if (measured.length > 0) {
|
|
139
|
+
facts.push(`Across ${measured.length} measured posts, average views = ${overallAvgViews}, average engagement rate = ${(overallEngagementRate * 100).toFixed(1)}%.`);
|
|
140
|
+
}
|
|
141
|
+
if (topProvider) {
|
|
142
|
+
const others = providers.filter((p) => p.provider !== topProvider.provider && p.posts >= 1);
|
|
143
|
+
const ratio = others.length > 0 && avg(others.map((o) => o.avgViews)) > 0
|
|
144
|
+
? round1(topProvider.avgViews / avg(others.map((o) => o.avgViews)))
|
|
145
|
+
: null;
|
|
146
|
+
facts.push(`${topProvider.provider} is your strongest platform (${topProvider.avgViews} avg views over ${topProvider.posts} posts${ratio && ratio > 1 ? `, ~${ratio}× your other platforms` : ""}).`);
|
|
147
|
+
}
|
|
148
|
+
if (bestTime) {
|
|
149
|
+
facts.push(`Posts published on weekday ${bestTime.weekday} around ${bestTime.hour}:00 (local) average ${bestTime.avgViews} views — your best slot.`);
|
|
150
|
+
}
|
|
151
|
+
if (dominantInteraction) {
|
|
152
|
+
facts.push(`Your audience ${dominantInteraction} more than anything else — ${dominantInteraction} dominate your engagement mix.`);
|
|
153
|
+
}
|
|
154
|
+
for (const lb of lengthBuckets) {
|
|
155
|
+
facts.push(`${lb.bucket}-length videos (${lb.bucket === "short" ? "<30s" : lb.bucket === "mid" ? "30–60s" : ">60s"}) average ${lb.avgViews} views over ${lb.posts} posts.`);
|
|
156
|
+
}
|
|
157
|
+
facts.push(`Posting cadence: ${postsLast7} post(s) in the last 7 days, ${postsLast30} in the last 30.`);
|
|
158
|
+
if (topPost && topPost.snippet) {
|
|
159
|
+
facts.push(`Top post (${topPost.views} views): "${topPost.snippet}".`);
|
|
160
|
+
}
|
|
161
|
+
return {
|
|
162
|
+
measuredPosts: measured.length,
|
|
163
|
+
overallAvgViews,
|
|
164
|
+
overallEngagementRate,
|
|
165
|
+
providers,
|
|
166
|
+
topProvider,
|
|
167
|
+
bestTime,
|
|
168
|
+
dominantInteraction,
|
|
169
|
+
postsLast7,
|
|
170
|
+
postsLast30,
|
|
171
|
+
topPost,
|
|
172
|
+
lengthBuckets,
|
|
173
|
+
facts,
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
const MAX_REC = 5;
|
|
177
|
+
const MAX_IDEA = 5;
|
|
178
|
+
const MAX_LINE = 240;
|
|
179
|
+
/** Build the chat prompt from the GROUNDED facts. Pure — no I/O. */
|
|
180
|
+
function buildRecommendationsPrompt(signals, language = "en") {
|
|
181
|
+
const lang = (language || "en").trim();
|
|
182
|
+
const system = "You are a social-media growth strategist writing a creator's weekly " +
|
|
183
|
+
"digest. You are given GROUND-TRUTH stats already computed from the " +
|
|
184
|
+
"creator's own posts — treat them as facts and NEVER invent numbers or " +
|
|
185
|
+
"claims beyond them. Turn them into a crisp summary, specific " +
|
|
186
|
+
"recommendations, and concrete next-post content ideas grounded in what's " +
|
|
187
|
+
`already working for THIS creator. Write in language "${lang}". Reply with ` +
|
|
188
|
+
"ONLY a single JSON object — no prose, no code fences.";
|
|
189
|
+
const factBlock = signals.facts.length
|
|
190
|
+
? signals.facts.map((f) => `- ${f}`).join("\n")
|
|
191
|
+
: "- (Not enough published history yet to compute trends.)";
|
|
192
|
+
const user = `Here are the creator's ground-truth stats:\n${factBlock}\n\n` +
|
|
193
|
+
"Write a 1-2 sentence summary, up to 5 specific recommendations (each tied " +
|
|
194
|
+
"to a stat above — what to do more/less of, when to post, which platform to " +
|
|
195
|
+
"lean into), and up to 5 next-post content ideas that build on what's " +
|
|
196
|
+
"working. If history is thin, say so plainly and give starter advice. Do " +
|
|
197
|
+
"not fabricate metrics.\n\n" +
|
|
198
|
+
"Reply with ONLY this JSON shape:\n" +
|
|
199
|
+
"{\n" +
|
|
200
|
+
' "summary": "string",\n' +
|
|
201
|
+
' "recommendations": ["..."],\n' +
|
|
202
|
+
' "contentIdeas": ["..."]\n' +
|
|
203
|
+
"}";
|
|
204
|
+
return [
|
|
205
|
+
{ role: "system", content: system },
|
|
206
|
+
{ role: "user", content: user },
|
|
207
|
+
];
|
|
208
|
+
}
|
|
209
|
+
function stripCodeFence(raw) {
|
|
210
|
+
const t = raw.trim();
|
|
211
|
+
if (!t.startsWith("```"))
|
|
212
|
+
return t;
|
|
213
|
+
return t.replace(/^```(?:json)?\s*/i, "").replace(/\s*```$/, "").trim();
|
|
214
|
+
}
|
|
215
|
+
function normalizeList(raw, max) {
|
|
216
|
+
if (!Array.isArray(raw))
|
|
217
|
+
return [];
|
|
218
|
+
const seen = new Set();
|
|
219
|
+
const out = [];
|
|
220
|
+
for (const item of raw) {
|
|
221
|
+
if (typeof item !== "string")
|
|
222
|
+
continue;
|
|
223
|
+
const s = item.replace(/\s+/g, " ").trim().slice(0, MAX_LINE);
|
|
224
|
+
if (!s)
|
|
225
|
+
continue;
|
|
226
|
+
const key = s.toLowerCase();
|
|
227
|
+
if (seen.has(key))
|
|
228
|
+
continue;
|
|
229
|
+
seen.add(key);
|
|
230
|
+
out.push(s);
|
|
231
|
+
if (out.length >= max)
|
|
232
|
+
break;
|
|
233
|
+
}
|
|
234
|
+
return out;
|
|
235
|
+
}
|
|
236
|
+
/**
|
|
237
|
+
* Parse the model's JSON into recommendations. Defensive: on malformed JSON it
|
|
238
|
+
* falls back to the deterministic facts as recommendations, so the panel always
|
|
239
|
+
* shows something TRUE (the facts can't be wrong — we computed them).
|
|
240
|
+
*/
|
|
241
|
+
function parseRecommendations(raw, signals) {
|
|
242
|
+
let obj = null;
|
|
243
|
+
try {
|
|
244
|
+
const parsed = JSON.parse(stripCodeFence(raw));
|
|
245
|
+
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
246
|
+
obj = parsed;
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
catch {
|
|
250
|
+
obj = null;
|
|
251
|
+
}
|
|
252
|
+
if (!obj) {
|
|
253
|
+
return {
|
|
254
|
+
summary: signals.measuredPosts > 0
|
|
255
|
+
? "Here's what your own numbers say so far."
|
|
256
|
+
: "Publish a few posts to unlock growth insights.",
|
|
257
|
+
recommendations: signals.facts.slice(0, MAX_REC),
|
|
258
|
+
contentIdeas: [],
|
|
259
|
+
};
|
|
260
|
+
}
|
|
261
|
+
return {
|
|
262
|
+
summary: typeof obj.summary === "string"
|
|
263
|
+
? obj.summary.replace(/\s+/g, " ").trim().slice(0, 400)
|
|
264
|
+
: "",
|
|
265
|
+
recommendations: normalizeList(obj.recommendations, MAX_REC),
|
|
266
|
+
contentIdeas: normalizeList(obj.contentIdeas, MAX_IDEA),
|
|
267
|
+
};
|
|
268
|
+
}
|
|
269
|
+
/**
|
|
270
|
+
* Generate growth recommendations with the LLM injected (testable). Phrases the
|
|
271
|
+
* pre-computed grounded signals; never throws on a bad response.
|
|
272
|
+
*/
|
|
273
|
+
async function generateRecommendationsCore(llm, args) {
|
|
274
|
+
const messages = buildRecommendationsPrompt(args.signals, args.language);
|
|
275
|
+
const raw = await llm(messages);
|
|
276
|
+
return parseRecommendations(raw, args.signals);
|
|
277
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* X8 Social Suite — Phase 6 best-time-to-post: per-user timezone bucketing.
|
|
3
|
+
*
|
|
4
|
+
* The best-time grid (C4) buckets each published post by (weekday, hour) and
|
|
5
|
+
* averages its views. v1 bucketed in UTC, which is wrong for the human question
|
|
6
|
+
* the feature answers — "what hour of *my* day should I post?". A creator in
|
|
7
|
+
* America/Los_Angeles posting at 6pm local was being filed under hour 02 (next
|
|
8
|
+
* day UTC), so the heatmap pointed at the wrong row AND the wrong column.
|
|
9
|
+
*
|
|
10
|
+
* This module is the pure, dependency-free core: given an epoch-ms timestamp and
|
|
11
|
+
* an IANA time-zone name, return the local weekday (0=Sun … 6=Sat) and hour
|
|
12
|
+
* (0–23). It uses `Intl.DateTimeFormat` (built into the runtime — no tz library)
|
|
13
|
+
* so it's deterministic and unit-testable away from any I/O. Invalid / absent
|
|
14
|
+
* zones fall back to UTC, preserving the old behaviour rather than throwing.
|
|
15
|
+
*/
|
|
16
|
+
/**
|
|
17
|
+
* Is `tz` a time zone the runtime's Intl can resolve? Empty / non-string / made
|
|
18
|
+
* up names return false (so the caller can fall back to UTC). Cheap enough to
|
|
19
|
+
* call per request — Intl caches its internal data.
|
|
20
|
+
*/
|
|
21
|
+
export declare function isValidTimeZone(tz: unknown): tz is string;
|
|
22
|
+
/**
|
|
23
|
+
* Bucket an epoch-ms timestamp into a LOCAL (weekday, hour) pair for the given
|
|
24
|
+
* IANA time zone. Falls back to UTC when `timeZone` is missing or invalid.
|
|
25
|
+
*
|
|
26
|
+
* @param epochMs unix milliseconds (the post's publish time)
|
|
27
|
+
* @param timeZone IANA name (e.g. "America/New_York"); undefined → UTC
|
|
28
|
+
*/
|
|
29
|
+
export declare function bucketLocal(epochMs: number, timeZone?: string): {
|
|
30
|
+
weekday: number;
|
|
31
|
+
hour: number;
|
|
32
|
+
};
|
|
33
|
+
/** The zone actually used after validation (echoed back to the client). */
|
|
34
|
+
export declare function resolveTimeZone(timeZone?: string): string;
|
|
35
|
+
//# sourceMappingURL=timeBuckets.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"timeBuckets.d.ts","sourceRoot":"","sources":["../../../src/services/socialInsights/timeBuckets.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAaH;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,EAAE,EAAE,OAAO,GAAG,EAAE,IAAI,MAAM,CAUzD;AAED;;;;;;GAMG;AACH,wBAAgB,WAAW,CACzB,OAAO,EAAE,MAAM,EACf,QAAQ,CAAC,EAAE,MAAM,GAChB;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAiBnC;AAED,2EAA2E;AAC3E,wBAAgB,eAAe,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,CAEzD"}
|