@gentbajko/slopify 3.25.9 → 4.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/adapters/account-limits.js +30 -0
- package/dist/adapters/alignment/cache.js +3 -5
- package/dist/adapters/alignment/ctc.js +1 -2
- package/dist/adapters/alignment/lock.js +4 -3
- package/dist/adapters/alignment/multilingual.js +0 -3
- package/dist/adapters/alignment/prefetch.js +2 -1
- package/dist/adapters/alignment/worker.js +2 -1
- package/dist/adapters/explain.js +0 -3
- package/dist/adapters/ffmpeg.js +19 -1
- package/dist/adapters/host-cli/index.js +19 -3
- package/dist/adapters/http-failure.js +53 -0
- package/dist/adapters/image/codex.js +36 -4
- package/dist/adapters/image/fal.js +17 -50
- package/dist/adapters/image/google.js +31 -55
- package/dist/adapters/image/openai.js +20 -47
- package/dist/adapters/image/reference.js +36 -0
- package/dist/adapters/image/replicate.js +18 -47
- package/dist/adapters/interest/search-interest.js +113 -0
- package/dist/adapters/key-probes.js +53 -2
- package/dist/adapters/llm/antigravity.js +4 -0
- package/dist/adapters/llm/assistant-cli.js +388 -0
- package/dist/adapters/llm/child-session.js +83 -0
- package/dist/adapters/llm/claude-code-models.js +51 -98
- package/dist/adapters/llm/claude-code.js +13 -2
- package/dist/adapters/llm/codex-limits.js +27 -68
- package/dist/adapters/llm/codex-server-models.js +61 -109
- package/dist/adapters/llm/codex.js +12 -1
- package/dist/adapters/llm/openrouter.js +10 -47
- package/dist/adapters/llm/run-cli.js +6 -22
- package/dist/adapters/llm/sse-lines.js +5 -1
- package/dist/adapters/model-access.js +37 -0
- package/dist/adapters/tts/cartesia.js +15 -46
- package/dist/adapters/tts/elevenlabs.js +20 -52
- package/dist/adapters/tts/gemini.js +19 -40
- package/dist/adapters/tts/inworld-async.js +4 -1
- package/dist/adapters/tts/inworld.js +3 -0
- package/dist/adapters/tts/openai.js +20 -50
- package/dist/assets/models-lifecycle.yaml +6 -0
- package/dist/assistant-wiring.js +60 -0
- package/dist/autopilot-timers.js +59 -0
- package/dist/catalog/lifecycle.js +52 -0
- package/dist/catalog/propose.js +204 -0
- package/dist/catalog/registry.js +3 -1
- package/dist/catalog/schema.js +2 -0
- package/dist/catalog/store.js +39 -0
- package/dist/edge/autostart/native.js +3 -5
- package/dist/edge/autostart/prompt.js +2 -1
- package/dist/edge/autostart/service.js +3 -4
- package/dist/edge/cli-args.js +3 -4
- package/dist/edge/cli.js +2 -1
- package/dist/edge/docker-install/apply.js +11 -11
- package/dist/edge/docker-install/run.js +2 -1
- package/dist/edge/docker-install/state.js +4 -3
- package/dist/edge/docker-install/tree.js +2 -4
- package/dist/edge/docker-install/volume.js +3 -2
- package/dist/edge/docker.js +5 -4
- package/dist/edge/events/hub.js +5 -5
- package/dist/edge/http/actions.js +4 -25
- package/dist/edge/http/actor.js +5 -3
- package/dist/edge/http/app.js +85 -10
- package/dist/edge/http/assistant.js +127 -0
- package/dist/edge/http/audio-preview.js +4 -11
- package/dist/edge/http/auditions.js +1 -1
- package/dist/edge/http/autostart.js +5 -5
- package/dist/edge/http/backups.js +1 -3
- package/dist/edge/http/channel-memory.js +7 -20
- package/dist/edge/http/channels.js +13 -26
- package/dist/edge/http/checkpoints.js +33 -17
- package/dist/edge/http/design-templates.js +167 -0
- package/dist/edge/http/designs.js +178 -0
- package/dist/edge/http/diagnostics.js +5 -1
- package/dist/edge/http/document-themes.js +2 -11
- package/dist/edge/http/draft-files.js +1 -3
- package/dist/edge/http/draft-problem.js +4 -9
- package/dist/edge/http/drafts.js +3 -3
- package/dist/edge/http/entries.js +6 -9
- package/dist/edge/http/extension-auth.js +27 -0
- package/dist/edge/http/external-reviews.js +12 -11
- package/dist/edge/http/files.js +5 -11
- package/dist/edge/http/folder-location.js +1 -4
- package/dist/edge/http/fonts.js +0 -7
- package/dist/edge/http/home.js +38 -13
- package/dist/edge/http/host-cli.js +18 -8
- package/dist/edge/http/ids.js +8 -0
- package/dist/edge/http/keywords.js +35 -0
- package/dist/edge/http/model-checks.js +132 -0
- package/dist/edge/http/narration-peaks.js +5 -13
- package/dist/edge/http/onboarding.js +1 -14
- package/dist/edge/http/open-folder.js +5 -10
- package/dist/edge/http/outputs.js +4 -12
- package/dist/edge/http/patch-notes.js +4 -10
- package/dist/edge/http/planning.js +2 -6
- package/dist/edge/http/problem.js +2 -4
- package/dist/edge/http/project-create.js +1 -0
- package/dist/edge/http/project-templates.js +29 -17
- package/dist/edge/http/projects.js +2 -12
- package/dist/edge/http/prompts.js +7 -14
- package/dist/edge/http/pronunciations.js +1 -2
- package/dist/edge/http/providers.js +39 -24
- package/dist/edge/http/refusal.js +29 -0
- package/dist/edge/http/remote-access.js +40 -0
- package/dist/edge/http/remote-guard.js +71 -0
- package/dist/edge/http/request-guard.js +91 -0
- package/dist/edge/http/reviews.js +11 -24
- package/dist/edge/http/revision-files.js +5 -10
- package/dist/edge/http/revisions.js +10 -15
- package/dist/edge/http/run-cost.js +1 -2
- package/dist/edge/http/same-page.js +80 -0
- package/dist/edge/http/schedules.js +59 -32
- package/dist/edge/http/settings.js +22 -13
- package/dist/edge/http/social.js +282 -0
- package/dist/edge/http/staging.js +2 -4
- package/dist/edge/http/storage-files.js +5 -7
- package/dist/edge/http/storage.js +23 -23
- package/dist/edge/http/studio.js +23 -50
- package/dist/edge/http/style-preview.js +1 -4
- package/dist/edge/http/subtitles.js +3 -17
- package/dist/edge/http/template-studio.js +433 -0
- package/dist/edge/http/trash.js +10 -11
- package/dist/edge/http/tutorial.js +1 -2
- package/dist/edge/http/tutorials.js +5 -11
- package/dist/edge/http/update.js +7 -13
- package/dist/edge/http/waveform.js +3 -6
- package/dist/edge/http/youtube-edits.js +2 -3
- package/dist/edge/mcp/assistant.js +279 -0
- package/dist/edge/mcp/attribution.js +8 -2
- package/dist/edge/mcp/call-route.js +11 -2
- package/dist/edge/mcp/excluded-routes.js +25 -0
- package/dist/edge/mcp/http.js +11 -2
- package/dist/edge/mcp/instructions.js +5 -3
- package/dist/edge/mcp/library-tools.js +1 -0
- package/dist/edge/mcp/route-tool.js +35 -15
- package/dist/edge/mcp/server.js +13 -8
- package/dist/edge/mcp/tools-channels.js +2 -0
- package/dist/edge/mcp/tools-designs.js +165 -0
- package/dist/edge/mcp/tools-library.js +14 -2
- package/dist/edge/mcp/tools-planning.js +23 -4
- package/dist/edge/mcp/tools-projects.js +28 -1
- package/dist/edge/mcp/tools-runs.js +14 -1
- package/dist/edge/mcp/tools-schedules.js +28 -4
- package/dist/edge/mcp/tools-setup.js +61 -3
- package/dist/edge/mcp/tools-social.js +46 -0
- package/dist/edge/mcp/tools-storage.js +8 -0
- package/dist/edge/mcp/tools-template-studio.js +166 -0
- package/dist/edge/mcp/tools.js +6 -0
- package/dist/edge/remote-access/cli.js +71 -0
- package/dist/edge/remote-access/model.js +32 -0
- package/dist/edge/remote-access/rules.js +207 -0
- package/dist/edge/remote-access/service.js +203 -0
- package/dist/edge/remote-access/store.js +44 -0
- package/dist/edge/remote-access/views.js +43 -0
- package/dist/extension/slopify-studio-chrome.zip +0 -0
- package/dist/extension/slopify-studio-firefox.zip +0 -0
- package/dist/host-cli/install.js +2 -2
- package/dist/host-cli/open-folder.js +2 -2
- package/dist/host-cli/paths.js +2 -4
- package/dist/host-cli/server.js +4 -4
- package/dist/host-cli/service.js +3 -3
- package/dist/host-cli/status.js +7 -7
- package/dist/kernel/db/migrations/0063-model-checks.sql +20 -0
- package/dist/kernel/db/migrations/0064-template-studio.sql +46 -0
- package/dist/kernel/db/migrations/0065-promise-and-assistant-reviews.sql +58 -0
- package/dist/kernel/db/migrations/0066-assistant.sql +44 -0
- package/dist/kernel/db/migrations/0067-autopilot.sql +6 -0
- package/dist/kernel/db/migrations/0068-topic-scores.sql +17 -0
- package/dist/kernel/db/migrations/0070-designs.sql +53 -0
- package/dist/kernel/db/migrations/0071-design-usage.sql +33 -0
- package/dist/kernel/db/migrations/0072-design-templates.sql +33 -0
- package/dist/kernel/db/migrations/0073-schedule-design-kind.sql +5 -0
- package/dist/kernel/db/migrations/0074-template-bakeoffs.sql +44 -0
- package/dist/kernel/db/migrations/0075-social-posts.sql +25 -0
- package/dist/kernel/db/migrations/0076-channel-social.sql +5 -0
- package/dist/kernel/db/migrations/0077-post-prompts.sql +17 -0
- package/dist/kernel/db/tx.js +2 -4
- package/dist/kernel/errors.js +27 -1
- package/dist/kernel/json-answer.js +15 -0
- package/dist/kernel/lock.js +4 -9
- package/dist/kernel/paths.js +8 -0
- package/dist/kernel/ports/assistant-cli.js +4 -0
- package/dist/kernel/ports/host-cli.js +3 -0
- package/dist/kernel/ports/interest.js +4 -0
- package/dist/kernel/ports/languages.js +0 -3
- package/dist/kernel/ports/model.js +2 -0
- package/dist/kernel/runner/attempt.js +3 -1
- package/dist/kernel/runner/audition.js +3 -1
- package/dist/kernel/runner/checkpoint-authority.js +2 -1
- package/dist/kernel/runner/index.js +8 -10
- package/dist/kernel/runner/model-access.js +40 -0
- package/dist/kernel/runner/model-check.js +153 -0
- package/dist/kernel/runner/providers.js +15 -10
- package/dist/kernel/runner/standalone.js +9 -5
- package/dist/main.js +334 -19
- package/dist/model-checks.js +166 -0
- package/dist/patch-notes/4.0.0.md +51 -0
- package/dist/patch-notes/index.json +6 -0
- package/dist/slices/admission/heads.js +2 -1
- package/dist/slices/admission/repo.js +2 -1
- package/dist/slices/admission/rules.js +43 -8
- package/dist/slices/admission/schema.js +8 -0
- package/dist/slices/admission/stage-writes.js +0 -3
- package/dist/slices/admission/start.js +48 -31
- package/dist/slices/admission/substitute.js +7 -4
- package/dist/slices/agents/attention-posts.js +31 -0
- package/dist/slices/agents/attention-templates.js +33 -0
- package/dist/slices/agents/attention.js +10 -1
- package/dist/slices/agents/connection.js +2 -6
- package/dist/slices/agents/model.js +6 -3
- package/dist/slices/agents/repo.js +15 -7
- package/dist/slices/article/continuation.js +0 -63
- package/dist/slices/article/plain.js +3 -2
- package/dist/slices/article/segments.js +2 -62
- package/dist/slices/assistant/access.js +1 -0
- package/dist/slices/assistant/asks.js +80 -0
- package/dist/slices/assistant/cost.js +54 -0
- package/dist/slices/assistant/gate.js +222 -0
- package/dist/slices/assistant/model.js +79 -0
- package/dist/slices/assistant/names.js +23 -0
- package/dist/slices/assistant/prompt.js +74 -0
- package/dist/slices/assistant/repo.js +74 -0
- package/dist/slices/assistant/tokens.js +18 -0
- package/dist/slices/assistant/turn.js +342 -0
- package/dist/slices/autopilot/buffer.js +76 -0
- package/dist/slices/autopilot/model.js +14 -0
- package/dist/slices/autopilot/notify.js +27 -0
- package/dist/slices/autopilot/repo.js +34 -0
- package/dist/slices/autopilot/signals.js +194 -0
- package/dist/slices/autopilot/warnings.js +85 -0
- package/dist/slices/autopilot/weekly.js +100 -0
- package/dist/slices/backups/files.js +2 -6
- package/dist/slices/backups/repo.js +3 -15
- package/dist/slices/backups/service.js +4 -3
- package/dist/slices/channels/cast-images.js +2 -1
- package/dist/slices/channels/model.js +0 -1
- package/dist/slices/channels/rebrand.js +0 -3
- package/dist/slices/channels/repo.js +4 -0
- package/dist/slices/channels/schema.js +6 -0
- package/dist/slices/channels/service.js +10 -0
- package/dist/slices/channels/videos.js +2 -14
- package/dist/slices/checkpoints/index.js +0 -1
- package/dist/slices/control/providers.js +0 -26
- package/dist/slices/designs/artwork.js +66 -0
- package/dist/slices/designs/budget.js +23 -0
- package/dist/slices/designs/calls.js +124 -0
- package/dist/slices/designs/check-context.js +38 -0
- package/dist/slices/designs/check-text.js +43 -0
- package/dist/slices/designs/fields.js +88 -0
- package/dist/slices/designs/frames.js +34 -0
- package/dist/slices/designs/gate.js +128 -0
- package/dist/slices/designs/layout.js +293 -0
- package/dist/slices/designs/model.js +35 -0
- package/dist/slices/designs/pdf-job.js +23 -0
- package/dist/slices/designs/pdf-pages.js +30 -0
- package/dist/slices/designs/pdf-runner.js +68 -0
- package/dist/slices/designs/pdf-worker.js +24 -0
- package/dist/slices/designs/picture-size.js +79 -0
- package/dist/slices/designs/pictures.js +55 -0
- package/dist/slices/designs/quote.js +24 -0
- package/dist/slices/designs/render-args.js +36 -0
- package/dist/slices/designs/render-input.js +59 -0
- package/dist/slices/designs/render.js +215 -0
- package/dist/slices/designs/repo.js +101 -0
- package/dist/slices/designs/requests.js +80 -0
- package/dist/slices/designs/runner.js +239 -0
- package/dist/slices/designs/sample-repo.js +91 -0
- package/dist/slices/designs/sample.js +222 -0
- package/dist/slices/designs/schema.js +87 -0
- package/dist/slices/designs/sections.js +44 -0
- package/dist/slices/designs/service.js +233 -0
- package/dist/slices/designs/steps-repo.js +102 -0
- package/dist/slices/designs/studio-kind.js +39 -0
- package/dist/slices/designs/template-checks.js +44 -0
- package/dist/slices/designs/template-make.js +118 -0
- package/dist/slices/designs/templates.js +218 -0
- package/dist/slices/document/fonts.js +2 -1
- package/dist/slices/episodes/summarize.js +5 -1
- package/dist/slices/estimate/index.js +45 -7
- package/dist/slices/fonts/catalog.js +3 -2
- package/dist/slices/fonts/discovery.js +3 -2
- package/dist/slices/fonts/files.js +2 -6
- package/dist/slices/fonts/index.js +0 -1
- package/dist/slices/fonts/sfnt.js +45 -10
- package/dist/slices/images/appearance.js +6 -15
- package/dist/slices/images/scenes.js +100 -27
- package/dist/slices/keywords/digest.js +84 -0
- package/dist/slices/keywords/facts.js +92 -0
- package/dist/slices/keywords/fill.js +80 -0
- package/dist/slices/keywords/model.js +75 -0
- package/dist/slices/keywords/rules.js +187 -0
- package/dist/slices/keywords/schema.js +28 -0
- package/dist/slices/keywords/specs.js +260 -0
- package/dist/slices/keywords/values.js +74 -0
- package/dist/slices/library/lint.js +29 -12
- package/dist/slices/library/model.js +2 -0
- package/dist/slices/library/photorealistic.js +4 -12
- package/dist/slices/library/rename.js +9 -7
- package/dist/slices/library/save.js +14 -10
- package/dist/slices/library/slots.js +37 -11
- package/dist/slices/library/transfer.js +2 -1
- package/dist/slices/library/used-by.js +25 -10
- package/dist/slices/loudness/loudnorm.js +51 -11
- package/dist/slices/model-checks/cost.js +61 -0
- package/dist/slices/model-checks/listing.js +124 -0
- package/dist/slices/model-checks/model.js +20 -0
- package/dist/slices/model-checks/record.js +32 -0
- package/dist/slices/model-checks/repo.js +120 -0
- package/dist/slices/model-checks/rules.js +112 -0
- package/dist/slices/model-checks/run.js +143 -0
- package/dist/slices/model-checks/service.js +198 -0
- package/dist/slices/model-upkeep/switch.js +70 -25
- package/dist/slices/model-upkeep/usage.js +26 -11
- package/dist/slices/narration/breaths.js +2 -1
- package/dist/slices/narration/clean-pieces.js +2 -1
- package/dist/slices/narration/concat.js +2 -1
- package/dist/slices/narration/peaks.js +3 -4
- package/dist/slices/narration/pronunciation-chunks.js +4 -3
- package/dist/slices/narration/pronunciation.js +3 -2
- package/dist/slices/narration/steering.js +4 -3
- package/dist/slices/notifications/notifier.js +4 -3
- package/dist/slices/notifications/rules.js +5 -2
- package/dist/slices/notifications/settings.js +4 -12
- package/dist/slices/notifications/subject.js +2 -1
- package/dist/slices/onboarding/copy.js +2 -1
- package/dist/slices/onboarding/first-run.js +3 -2
- package/dist/slices/onboarding/full-video.js +3 -2
- package/dist/slices/onboarding/install.js +4 -3
- package/dist/slices/onboarding/model.js +0 -16
- package/dist/slices/onboarding/packs.js +22 -22
- package/dist/slices/onboarding/quick-short.js +2 -1
- package/dist/slices/onboarding/sample.js +0 -1
- package/dist/slices/onboarding/state.js +2 -11
- package/dist/slices/onboarding/template.js +2 -1
- package/dist/slices/patch-notes/library.js +3 -2
- package/dist/slices/play-drafts/convert.js +4 -0
- package/dist/slices/play-drafts/review-inputs.js +8 -2
- package/dist/slices/play-drafts/review.js +5 -4
- package/dist/slices/play-drafts/schema.js +4 -0
- package/dist/slices/project-templates/from-project.js +9 -2
- package/dist/slices/project-templates/repo.js +6 -1
- package/dist/slices/project-templates/setup.js +8 -0
- package/dist/slices/project-templates/stand-ins.js +22 -0
- package/dist/slices/rebuild/activity.js +2 -0
- package/dist/slices/rebuild/admission-repo.js +3 -2
- package/dist/slices/rebuild/narration-history.js +4 -5
- package/dist/slices/rebuild/narration-retry.js +2 -1
- package/dist/slices/rebuild/preview-details.js +27 -9
- package/dist/slices/rebuild/preview-plan.js +3 -8
- package/dist/slices/rebuild/recipe-build.js +8 -3
- package/dist/slices/rebuild/recipe-describe.js +3 -3
- package/dist/slices/rebuild/recipe-digest.js +58 -0
- package/dist/slices/rebuild/recipe-input-schema.js +10 -0
- package/dist/slices/rebuild/recipe-keywords.js +71 -0
- package/dist/slices/rebuild/recipe-legacy.js +5 -1
- package/dist/slices/rebuild/recipe-model.js +4 -0
- package/dist/slices/rebuild/recipe-narration-text.js +2 -1
- package/dist/slices/rebuild/recipe-preparation.js +3 -3
- package/dist/slices/rebuild/recipe-reference.js +8 -4
- package/dist/slices/rebuild/recipe-reviews.js +27 -7
- package/dist/slices/rebuild/recipe-save.js +6 -3
- package/dist/slices/rebuild/recipe-scenes.js +40 -11
- package/dist/slices/rebuild/recipe-shorts.js +8 -8
- package/dist/slices/rebuild/recipe-social.js +46 -0
- package/dist/slices/rebuild/recipe-text.js +64 -20
- package/dist/slices/rebuild/recipe-validation.js +98 -28
- package/dist/slices/rebuild/recipe-visual.js +55 -16
- package/dist/slices/rebuild/recipe-work.js +15 -3
- package/dist/slices/rebuild/recipe-youtube.js +4 -3
- package/dist/slices/rebuild/recovery-selection.js +3 -1
- package/dist/slices/rebuild/recovery.js +3 -2
- package/dist/slices/rebuild/repo.js +4 -3
- package/dist/slices/rebuild/review-promise.js +25 -0
- package/dist/slices/rebuild/review-redo.js +2 -1
- package/dist/slices/rebuild/runtime-admission.js +4 -3
- package/dist/slices/rebuild/runtime-agent-review.js +11 -5
- package/dist/slices/rebuild/runtime-animate.js +9 -8
- package/dist/slices/rebuild/runtime-article.js +3 -2
- package/dist/slices/rebuild/runtime-checkpoints.js +5 -4
- package/dist/slices/rebuild/runtime-document.js +8 -11
- package/dist/slices/rebuild/runtime-export-bed.js +6 -11
- package/dist/slices/rebuild/runtime-export-edit.js +6 -15
- package/dist/slices/rebuild/runtime-export-inputs.js +7 -7
- package/dist/slices/rebuild/runtime-export-short.js +8 -4
- package/dist/slices/rebuild/runtime-export.js +34 -12
- package/dist/slices/rebuild/runtime-figure-card.js +2 -1
- package/dist/slices/rebuild/runtime-image.js +4 -9
- package/dist/slices/rebuild/runtime-instructions.js +2 -0
- package/dist/slices/rebuild/runtime-keywords.js +59 -0
- package/dist/slices/rebuild/runtime-lines.js +2 -11
- package/dist/slices/rebuild/runtime-local.js +14 -20
- package/dist/slices/rebuild/runtime-narration-publication.js +2 -5
- package/dist/slices/rebuild/runtime-narration-reuse.js +9 -13
- package/dist/slices/rebuild/runtime-narration-text.js +10 -15
- package/dist/slices/rebuild/runtime-pictures.js +2 -4
- package/dist/slices/rebuild/runtime-piece-label.js +10 -6
- package/dist/slices/rebuild/runtime-plan.js +31 -5
- package/dist/slices/rebuild/runtime-provider.js +60 -19
- package/dist/slices/rebuild/runtime-publication.js +10 -9
- package/dist/slices/rebuild/runtime-review.js +33 -28
- package/dist/slices/rebuild/runtime-run.js +4 -1
- package/dist/slices/rebuild/runtime-shorts.js +25 -45
- package/dist/slices/rebuild/runtime-store.js +3 -8
- package/dist/slices/rebuild/runtime-subtitles.js +16 -14
- package/dist/slices/rebuild/runtime-voices.js +8 -15
- package/dist/slices/rebuild/runtime-youtube.js +11 -29
- package/dist/slices/rebuild/transition-repo.js +6 -5
- package/dist/slices/rebuild/work-records.js +6 -0
- package/dist/slices/reruns/cascade.js +1 -2
- package/dist/slices/reruns/index.js +6 -9
- package/dist/slices/reviews/external-model.js +3 -0
- package/dist/slices/reviews/external-repo.js +2 -1
- package/dist/slices/reviews/external-rows.js +2 -3
- package/dist/slices/reviews/model.js +26 -8
- package/dist/slices/reviews/repo.js +2 -1
- package/dist/slices/reviews/rules.js +1 -0
- package/dist/slices/reviews/view.js +5 -2
- package/dist/slices/revisions/downloads.js +4 -5
- package/dist/slices/revisions/mutation-assets.js +7 -4
- package/dist/slices/revisions/mutation-cues.js +2 -5
- package/dist/slices/revisions/mutations.js +0 -1
- package/dist/slices/revisions/repo.js +3 -1
- package/dist/slices/revisions/schema.js +1 -0
- package/dist/slices/revisions/view.js +20 -0
- package/dist/slices/run-cost/limits.js +5 -2
- package/dist/slices/run-cost/meter.js +12 -5
- package/dist/slices/run-cost/panel.js +8 -74
- package/dist/slices/run-cost/plan-readings.js +127 -0
- package/dist/slices/run-cost/plan-use.js +169 -0
- package/dist/slices/run-cost/pricing.js +3 -3
- package/dist/slices/run-cost/week.js +32 -4
- package/dist/slices/schedules/agenda.js +11 -2
- package/dist/slices/schedules/by-date.js +42 -0
- package/dist/slices/schedules/calendar.js +5 -0
- package/dist/slices/schedules/design-agenda.js +36 -0
- package/dist/slices/schedules/design-check.js +41 -0
- package/dist/slices/schedules/design-run.js +126 -0
- package/dist/slices/schedules/next-run.js +62 -0
- package/dist/slices/schedules/prepare.js +18 -2
- package/dist/slices/schedules/repo.js +24 -16
- package/dist/slices/schedules/rescore.js +35 -0
- package/dist/slices/schedules/scheduler.js +13 -3
- package/dist/slices/schedules/schema.js +54 -3
- package/dist/slices/schedules/score.js +107 -0
- package/dist/slices/schedules/service.js +57 -24
- package/dist/slices/schedules/topic-list.js +15 -0
- package/dist/slices/schedules/topic-scores.js +104 -0
- package/dist/slices/schedules/topics.js +48 -36
- package/dist/slices/settings/cli-paths.js +4 -6
- package/dist/slices/settings/first-run.js +2 -11
- package/dist/slices/settings/model-reach.js +3 -17
- package/dist/slices/settings/model.js +10 -0
- package/dist/slices/settings/playback.js +33 -22
- package/dist/slices/settings/probe-read.js +16 -0
- package/dist/slices/settings/repo.js +33 -0
- package/dist/slices/settings/tutorial.js +2 -2
- package/dist/slices/settings/voices.js +4 -12
- package/dist/slices/shorts/pick.js +2 -15
- package/dist/slices/shorts/prompts.js +2 -13
- package/dist/slices/shorts/render.js +3 -0
- package/dist/slices/social/channel.js +44 -0
- package/dist/slices/social/defaults.js +17 -0
- package/dist/slices/social/due.js +36 -0
- package/dist/slices/social/extension.js +18 -0
- package/dist/slices/social/fit.js +115 -0
- package/dist/slices/social/limits.js +186 -0
- package/dist/slices/social/links.js +21 -0
- package/dist/slices/social/media.js +131 -0
- package/dist/slices/social/model.js +75 -0
- package/dist/slices/social/notice.js +19 -0
- package/dist/slices/social/pack.js +176 -0
- package/dist/slices/social/post-prompt.js +55 -0
- package/dist/slices/social/queue.js +31 -0
- package/dist/slices/social/repo.js +133 -0
- package/dist/slices/social/reshape.js +97 -0
- package/dist/slices/social/rules.js +5 -0
- package/dist/slices/social/schema.js +70 -0
- package/dist/slices/social/serve.js +51 -0
- package/dist/slices/social/service.js +128 -0
- package/dist/slices/social/settings.js +21 -0
- package/dist/slices/social/sources.js +114 -0
- package/dist/slices/social/state.js +110 -0
- package/dist/slices/social/text.js +149 -0
- package/dist/slices/storage/arrange.js +3 -1
- package/dist/slices/storage/assets.js +8 -10
- package/dist/slices/storage/backup-designs.js +74 -0
- package/dist/slices/storage/backup-export.js +8 -3
- package/dist/slices/storage/backup-format.js +10 -0
- package/dist/slices/storage/backup-import.js +20 -2
- package/dist/slices/storage/delete-project.js +6 -5
- package/dist/slices/storage/files-location.js +6 -8
- package/dist/slices/storage/layout.js +13 -2
- package/dist/slices/storage/model.js +3 -0
- package/dist/slices/storage/portable.js +16 -11
- package/dist/slices/storage/schema.js +2 -0
- package/dist/slices/storage/staging-refs.js +2 -1
- package/dist/slices/storage/staging.js +6 -9
- package/dist/slices/storage/trim.js +10 -3
- package/dist/slices/studio/calendar.js +4 -1
- package/dist/slices/studio/pick.js +2 -11
- package/dist/slices/studio/plan.js +6 -15
- package/dist/slices/studio/queue.js +3 -14
- package/dist/slices/studio/releases.js +26 -0
- package/dist/slices/studio/settings.js +14 -51
- package/dist/slices/studio/videos.js +12 -0
- package/dist/slices/style-preview/render.js +34 -13
- package/dist/slices/style-preview/schema.js +13 -0
- package/dist/slices/style-preview/service.js +2 -1
- package/dist/slices/style-preview/settings.js +33 -5
- package/dist/slices/telemetry/collector-client.js +2 -4
- package/dist/slices/telemetry/flush.js +2 -4
- package/dist/slices/telemetry/model.js +0 -7
- package/dist/slices/telemetry/record.js +10 -3
- package/dist/slices/template-studio/anatomy-view.js +118 -0
- package/dist/slices/template-studio/anatomy.js +73 -0
- package/dist/slices/template-studio/bakeoff-make.js +225 -0
- package/dist/slices/template-studio/bakeoff-repo.js +83 -0
- package/dist/slices/template-studio/bakeoff.js +333 -0
- package/dist/slices/template-studio/budget.js +103 -0
- package/dist/slices/template-studio/checks.js +273 -0
- package/dist/slices/template-studio/document-sample.js +56 -0
- package/dist/slices/template-studio/edit.js +15 -0
- package/dist/slices/template-studio/for-all.js +161 -0
- package/dist/slices/template-studio/free-video.js +31 -0
- package/dist/slices/template-studio/free.js +176 -0
- package/dist/slices/template-studio/gate.js +40 -0
- package/dist/slices/template-studio/hidden.js +20 -0
- package/dist/slices/template-studio/jobs.js +79 -0
- package/dist/slices/template-studio/loudness.js +111 -0
- package/dist/slices/template-studio/make.js +40 -0
- package/dist/slices/template-studio/model.js +54 -0
- package/dist/slices/template-studio/pack-import.js +253 -0
- package/dist/slices/template-studio/pack-samples.js +60 -0
- package/dist/slices/template-studio/pack.js +205 -0
- package/dist/slices/template-studio/paid-view.js +66 -0
- package/dist/slices/template-studio/paid.js +348 -0
- package/dist/slices/template-studio/quote.js +148 -0
- package/dist/slices/template-studio/ready.js +104 -0
- package/dist/slices/template-studio/repo.js +134 -0
- package/dist/slices/template-studio/reviews-path.js +58 -0
- package/dist/slices/template-studio/runner.js +84 -0
- package/dist/slices/template-studio/save.js +236 -0
- package/dist/slices/template-studio/say-it-as.js +248 -0
- package/dist/slices/template-studio/schedule-gate.js +34 -0
- package/dist/slices/template-studio/schedule-price.js +30 -0
- package/dist/slices/template-studio/schedule-sample.js +87 -0
- package/dist/slices/template-studio/schema.js +218 -0
- package/dist/slices/template-studio/scope.js +124 -0
- package/dist/slices/template-studio/sections.js +176 -0
- package/dist/slices/template-studio/service.js +173 -0
- package/dist/slices/template-studio/status.js +174 -0
- package/dist/slices/template-studio/topic.js +223 -0
- package/dist/slices/trash/designs.js +76 -0
- package/dist/slices/trash/model.js +13 -4
- package/dist/slices/trash/service.js +52 -2
- package/dist/slices/tutorials/library.js +3 -2
- package/dist/slices/video/ass-text.js +17 -0
- package/dist/slices/video/audio-export.js +2 -64
- package/dist/slices/video/encoding.js +160 -0
- package/dist/slices/video/ffmpeg.js +23 -21
- package/dist/slices/video/figure-card.js +1 -15
- package/dist/slices/video/slideshow.js +43 -18
- package/dist/slices/voices/palette.js +0 -3
- package/dist/slices/youtube/answer.js +2 -15
- package/dist/slices/youtube/edits-repo.js +2 -11
- package/dist/tutorials/Agents-MCP.md +2 -1
- package/dist/tutorials/Assistant.md +42 -0
- package/dist/tutorials/Channels.md +2 -0
- package/dist/tutorials/Home-and-Projects.md +19 -0
- package/dist/tutorials/Install.md +1 -0
- package/dist/tutorials/Keywords-Slopify-Fills.md +87 -0
- package/dist/tutorials/Models.md +33 -1
- package/dist/tutorials/Notifications.md +6 -0
- package/dist/tutorials/Play-Images.md +4 -0
- package/dist/tutorials/Posting-to-Social-Sites.md +121 -0
- package/dist/tutorials/Prompts.md +10 -6
- package/dist/tutorials/Remote-Access.md +63 -0
- package/dist/tutorials/Reviews-and-Checkpoints.md +13 -2
- package/dist/tutorials/Schedules.md +21 -1
- package/dist/tutorials/Settings-Reference.md +50 -8
- package/dist/tutorials/Template-Studio.md +125 -0
- package/dist/tutorials/Templates.md +3 -0
- package/dist/tutorials/_Sidebar.md +5 -0
- package/dist/updater/work-in-progress.js +42 -12
- package/dist/web/assets/index-CkAI_2E7.css +1 -0
- package/dist/web/assets/index-DcD0cdfo.js +334 -0
- package/dist/web/assets/{pdf-T7MCwjIv.js → pdf-TODRwF31.js} +1 -1
- package/dist/web/index.html +2 -2
- package/package.json +5 -1
- package/dist/slices/article/provided-entries.js +0 -66
- package/dist/slices/article/run.js +0 -145
- package/dist/slices/images/run.js +0 -234
- package/dist/slices/narration/legacy-plan.js +0 -58
- package/dist/slices/narration/run.js +0 -270
- package/dist/slices/rebuild/runtime-actions.js +0 -90
- package/dist/slices/research/run.js +0 -224
- package/dist/slices/subtitles/prepare.js +0 -194
- package/dist/slices/subtitles/transcript.js +0 -34
- package/dist/slices/thumbnail/run.js +0 -219
- package/dist/slices/video/audio-inputs.js +0 -33
- package/dist/slices/video/reuse-audio.js +0 -62
- package/dist/slices/video/run.js +0 -78
- package/dist/slices/video/write-export.js +0 -158
- package/dist/slices/video/write-subtitles.js +0 -59
- package/dist/web/assets/index-BadsoORk.css +0 -1
- package/dist/web/assets/index-D8LqNsCl.js +0 -326
|
@@ -0,0 +1,348 @@
|
|
|
1
|
+
import { setTimeout as sleep } from "node:timers/promises";
|
|
2
|
+
import { transact } from "../../kernel/db/tx.js";
|
|
3
|
+
import { internalError } from "../../kernel/errors.js";
|
|
4
|
+
import { derive } from "../../kernel/runner/graph.js";
|
|
5
|
+
import { pendingAdmitted } from "../admission/admitted-work.js";
|
|
6
|
+
import { projectExists, projectPaused, stagesOf } from "../admission/repo.js";
|
|
7
|
+
import { startRun } from "../admission/start.js";
|
|
8
|
+
import { estimateRun } from "../estimate/index.js";
|
|
9
|
+
import { checkDraftReadiness } from "../play-drafts/readiness.js";
|
|
10
|
+
import { templateById } from "../project-templates/repo.js";
|
|
11
|
+
import { recoverProject } from "../rebuild/recovery.js";
|
|
12
|
+
import { previewRebuild, startRebuild } from "../rebuild/service.js";
|
|
13
|
+
import { saveRevision } from "../revisions/mutations.js";
|
|
14
|
+
import { currentRevisionId } from "../revisions/repo.js";
|
|
15
|
+
import { getRevisionView } from "../revisions/view.js";
|
|
16
|
+
import { budgetRoom, budgetWords, fitsBudget } from "./budget.js";
|
|
17
|
+
import { sampleEdit } from "./edit.js";
|
|
18
|
+
import { removeHiddenProject } from "./hidden.js";
|
|
19
|
+
import { continueJobAsSave } from "./jobs.js";
|
|
20
|
+
import { resolveSampleRun, totals } from "./quote.js";
|
|
21
|
+
import { sampleOf, sampleVersions, setSampleVersion, upsertSample } from "./repo.js";
|
|
22
|
+
import { exceedsAcknowledged } from "./save.js";
|
|
23
|
+
import { sampleDocument } from "./scope.js";
|
|
24
|
+
import { defaultSampleTopic } from "./service.js";
|
|
25
|
+
const money = (value) => `$${value.toFixed(2)}`;
|
|
26
|
+
// Why a sample of about `high` (with `unknown` steps unpriced) waits instead of starting, or
|
|
27
|
+
// undefined when it may start.
|
|
28
|
+
export function holdReason(deps, job, cost) {
|
|
29
|
+
if (exceedsAcknowledged(cost.high, job.ackHigh))
|
|
30
|
+
return `Waiting: this sample now costs about ${money(cost.high)}, more than the ${money(job.ackHigh)} you saw. Press Make sample (${money(cost.high)}) to make it.`;
|
|
31
|
+
if (cost.unknown > 0 && !deps.acks.accepted(job.templateId, job.templateVersion))
|
|
32
|
+
return `Waiting: ${String(cost.unknown)} ${cost.unknown === 1 ? "step of this sample has" : "steps of this sample have"} no known price. Press Make sample to accept that and make it.`;
|
|
33
|
+
const room = budgetRoom(deps.db, deps.now, deps.timeZone);
|
|
34
|
+
if (!fitsBudget(room, cost.high, cost.usesPlan))
|
|
35
|
+
return `Waiting: ${budgetWords(room, cost, "this sample")}, or press Make sample tomorrow.`;
|
|
36
|
+
return undefined;
|
|
37
|
+
}
|
|
38
|
+
// Whether the sample project still has work going: a stage running or waiting, or work admitted
|
|
39
|
+
// and not yet finished while nothing failed, was canceled or paused. A step still waiting counts
|
|
40
|
+
// only while its stage is unfinished: a run's placeholder for work it turned out not to need
|
|
41
|
+
// (no figure to describe, say) stays pending on a finished stage, and nothing will ever run it.
|
|
42
|
+
export function sampleBusy(db, projectId) {
|
|
43
|
+
const state = derive(stagesOf(db, projectId), projectPaused(db, projectId));
|
|
44
|
+
if (state === "running")
|
|
45
|
+
return true;
|
|
46
|
+
if (state === "failed" || state === "canceled" || state === "paused")
|
|
47
|
+
return false;
|
|
48
|
+
return (db
|
|
49
|
+
.prepare(`SELECT 1 FROM revision_work w WHERE w.project_id=? AND w.dispatch_state='allowed'
|
|
50
|
+
AND (w.state='running' OR (w.state='pending' AND EXISTS (SELECT 1 FROM stages s
|
|
51
|
+
WHERE s.id=w.stage_id AND s.state NOT IN ('done','provided','skipped')))) LIMIT 1`)
|
|
52
|
+
.get(projectId) !== undefined);
|
|
53
|
+
}
|
|
54
|
+
// Resolves once the project has no work going (`sampleBusy`) and no call in flight.
|
|
55
|
+
export async function settled(deps, projectId, signal) {
|
|
56
|
+
while (sampleBusy(deps.start.db, projectId) ||
|
|
57
|
+
deps.start.runner.hasInflight?.(projectId) === true)
|
|
58
|
+
await sleep(deps.pollMs ?? 2000, undefined, { signal });
|
|
59
|
+
}
|
|
60
|
+
// Whether a restart stopped the project's run short: a step that was running when Slopify quit
|
|
61
|
+
// failed as interrupted, or its admitted work is held until the run is continued.
|
|
62
|
+
function stoppedByRestart(db, projectId) {
|
|
63
|
+
return (stagesOf(db, projectId).some((stage) => stage.state === "failed" && stage.failureReason === "interrupted") ||
|
|
64
|
+
db
|
|
65
|
+
.prepare(`SELECT 1 FROM revision_work w WHERE w.project_id=? AND w.dispatch_state='held'
|
|
66
|
+
AND ${pendingAdmitted("w")} LIMIT 1`)
|
|
67
|
+
.get(projectId) !== undefined);
|
|
68
|
+
}
|
|
69
|
+
// What the project's calls billed per use cost since this version's sample started on it.
|
|
70
|
+
function spentSince(db, projectId, version) {
|
|
71
|
+
const row = db
|
|
72
|
+
.prepare("SELECT COALESCE(SUM(cost),0) AS spent FROM provider_usage WHERE project_id=? AND on_plan=0 AND created_at>=?")
|
|
73
|
+
.get(projectId, version.updatedAt);
|
|
74
|
+
return typeof row?.spent === "number" ? row.spent : 0;
|
|
75
|
+
}
|
|
76
|
+
// What the finished sample says: done, or the failed step's own sentence.
|
|
77
|
+
function outcome(db, projectId) {
|
|
78
|
+
const stages = stagesOf(db, projectId);
|
|
79
|
+
const state = derive(stages, projectPaused(db, projectId));
|
|
80
|
+
if (state === "failed") {
|
|
81
|
+
const reason = stages.find((stage) => stage.state === "failed")?.failureReason;
|
|
82
|
+
return {
|
|
83
|
+
failed: (reason === undefined || reason === null ? undefined : studioWords(reason)) ??
|
|
84
|
+
"A step of the sample failed without saying why. Press Make sample to try again; if it fails again, use Download diagnostics in Settings and report it.",
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
if (state === "canceled" || state === "paused")
|
|
88
|
+
return { failed: "The sample stopped before it finished. Press Make sample to make it again." };
|
|
89
|
+
return { failed: null };
|
|
90
|
+
}
|
|
91
|
+
// A step's sentence names a project's Try again (on its stage); a studio's own button for it
|
|
92
|
+
// is another: Make sample for a sample, Compare again for a model comparison. `then` is what
|
|
93
|
+
// follows the button's name in the sentence.
|
|
94
|
+
export function studioWords(reason, button = "Make sample", then = " to try again") {
|
|
95
|
+
return reason
|
|
96
|
+
.replace(/choose another model in the Providers section of Edit project/g, "choose another model with Change on that section's “Made with …” line")
|
|
97
|
+
.replace(/(\b[UuPp](?:se|ress) )?Try again(?: on [A-Z][a-z]+)?/g, (_, verb, at) => {
|
|
98
|
+
const opens = verb === undefined ? /(^|[.;:!?]\s+)$/.test(reason.slice(0, at)) : /^[UP]/.test(verb);
|
|
99
|
+
return `${opens ? "Press" : "press"} ${button}${then}`;
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
const refusedSentence = {
|
|
103
|
+
conflict: "The sample project changed while the sample was being set up. Press Make sample again.",
|
|
104
|
+
"stale-preview": "What the sample needed changed while it was being set up. Press Make sample again.",
|
|
105
|
+
readiness: "A provider this template uses isn't set up, so the sample can't start. Set it up in Settings → Providers, then press Make sample.",
|
|
106
|
+
"cost-ack-required": "Some steps of this sample have no known price. Press Make sample to accept that and make it.",
|
|
107
|
+
};
|
|
108
|
+
export function refused(reason, fields) {
|
|
109
|
+
const detail = [...new Set((fields ?? []).map((field) => field.message))].join(" ");
|
|
110
|
+
return (refusedSentence[reason] ??
|
|
111
|
+
(detail === ""
|
|
112
|
+
? "Slopify couldn't start the sample. Press Make sample again; if it fails again, use Download diagnostics in Settings and report it."
|
|
113
|
+
: `The sample couldn't start: ${detail}`));
|
|
114
|
+
}
|
|
115
|
+
export async function makePaidSample(deps, job, signal) {
|
|
116
|
+
const { db } = deps.start;
|
|
117
|
+
const at = () => deps.start.clock.now().toISOString();
|
|
118
|
+
const template = templateById(db, job.templateId, job.templateVersion);
|
|
119
|
+
if (template === undefined)
|
|
120
|
+
return;
|
|
121
|
+
const mark = (change) => setSampleVersion(db, job.templateId, job.templateVersion, { ...change, at: at() });
|
|
122
|
+
const record = sampleOf(db, job.templateId);
|
|
123
|
+
const topic = record?.topic ?? defaultSampleTopic(db);
|
|
124
|
+
const topicSource = record?.topicSource ?? "fixed";
|
|
125
|
+
const fullScript = record?.fullScript ?? false;
|
|
126
|
+
const resolved = await resolveSampleRun(deps.start, sampleDocument(template.document, { topic, fullScript }), fullScript);
|
|
127
|
+
if (!resolved.ok) {
|
|
128
|
+
mark({
|
|
129
|
+
state: "failed",
|
|
130
|
+
heldReason: `${resolved.problem} Change it in the studio, then press Save.`,
|
|
131
|
+
});
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
const { run, words } = resolved;
|
|
135
|
+
const hold = (cost) => holdReason({ db, now: deps.start.clock.now(), acks: deps.acks, timeZone: deps.timeZone }, job, cost);
|
|
136
|
+
let projectId = record !== undefined && projectExists(db, record.projectId) ? record.projectId : undefined;
|
|
137
|
+
if (projectId !== undefined) {
|
|
138
|
+
// Work the last sample started finishes first: it is never stopped from here.
|
|
139
|
+
await settled(deps, projectId, signal);
|
|
140
|
+
// This version's sample already started on that project (a restart queued its job again):
|
|
141
|
+
// it is continued there, never made again on a new one.
|
|
142
|
+
const head = currentRevisionId(db, projectId);
|
|
143
|
+
const version = sampleVersions(db, job.templateId).find((one) => one.templateVersion === job.templateVersion);
|
|
144
|
+
const stopped = stoppedByRestart(db, projectId);
|
|
145
|
+
// Make sample pressed again after this version's run failed: the run goes on from the
|
|
146
|
+
// failed step, as Continue the run does on a project. A revision saved over it instead left
|
|
147
|
+
// the steps after the failed one admitted on the old revision, where nothing ran them.
|
|
148
|
+
// Make sample has queued the version again by then, so the project's own state says so.
|
|
149
|
+
const failedHere = derive(stagesOf(db, projectId), projectPaused(db, projectId)) === "failed";
|
|
150
|
+
if (head !== undefined &&
|
|
151
|
+
version?.revisionId === head &&
|
|
152
|
+
(version.state === "making" || stopped || failedHere)) {
|
|
153
|
+
if (stopped || failedHere) {
|
|
154
|
+
const usesPlan = totals(estimateRun(run.draft, run.rendered, words, deps.start.catalogue).rows).usesPlan;
|
|
155
|
+
const left = Math.max(0, (version.quotedHigh ?? 0) - spentSince(db, projectId, version));
|
|
156
|
+
const waiting = hold({ high: left, unknown: 0, usesPlan });
|
|
157
|
+
if (waiting !== undefined) {
|
|
158
|
+
mark({ state: "held", heldReason: waiting });
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
const resumed = await recoverProject(deps.rebuild, projectId, {
|
|
162
|
+
baseRevisionId: head,
|
|
163
|
+
idempotencyKey: deps.start.uuid(),
|
|
164
|
+
action: { kind: "resume" },
|
|
165
|
+
});
|
|
166
|
+
if (!resumed.ok) {
|
|
167
|
+
mark({
|
|
168
|
+
state: "failed",
|
|
169
|
+
heldReason: stopped
|
|
170
|
+
? `The sample stopped when Slopify quit, and couldn't go on: ${refused(resumed.reason, resumed.fields)}`
|
|
171
|
+
: `The sample's failed step couldn't be tried again: ${refused(resumed.reason, resumed.fields)}`,
|
|
172
|
+
});
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
175
|
+
mark({ state: "making", heldReason: null });
|
|
176
|
+
}
|
|
177
|
+
await finish(deps, job, projectId, head, signal);
|
|
178
|
+
return;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
// The sample project a new one replaces. It is removed only once the new one actually
|
|
182
|
+
// starts: a change that waits or can't start keeps the old sample as it was.
|
|
183
|
+
let replaced;
|
|
184
|
+
if (projectId !== undefined && job.kind === "topic") {
|
|
185
|
+
// A new topic is a new video: the sample starts again on a project of its own, so no
|
|
186
|
+
// step keeps the old topic as its subject.
|
|
187
|
+
replaced = {
|
|
188
|
+
projectId,
|
|
189
|
+
sentence: "The new topic needs a new sample project, and the old one's files couldn't be removed. Press Make sample again; if it fails again, use Download diagnostics in Settings and report it.",
|
|
190
|
+
};
|
|
191
|
+
projectId = undefined;
|
|
192
|
+
}
|
|
193
|
+
if (projectId !== undefined) {
|
|
194
|
+
const base = getRevisionView(deps.rebuild, projectId, currentRevisionId(db, projectId) ?? "");
|
|
195
|
+
if (base === undefined)
|
|
196
|
+
projectId = undefined;
|
|
197
|
+
else {
|
|
198
|
+
const saved = await saveRevision(deps.rebuild, {
|
|
199
|
+
projectId,
|
|
200
|
+
baseRevisionId: base.revision.id,
|
|
201
|
+
idempotencyKey: deps.start.uuid(),
|
|
202
|
+
edit: sampleEdit(deps.rebuild, base, run),
|
|
203
|
+
});
|
|
204
|
+
if (!saved.ok && saved.reason === "invalid-edit") {
|
|
205
|
+
// A change the project's editor can't make (a different format, a step switched on
|
|
206
|
+
// that a revision can't add): the sample starts again on a project of its own.
|
|
207
|
+
replaced = {
|
|
208
|
+
projectId,
|
|
209
|
+
sentence: "The sample needs a new project, and the old one's files couldn't be removed. Press Make sample again; if it fails again, use Download diagnostics in Settings and report it.",
|
|
210
|
+
};
|
|
211
|
+
projectId = undefined;
|
|
212
|
+
}
|
|
213
|
+
else if (!saved.ok) {
|
|
214
|
+
mark({ state: "failed", heldReason: refused(saved.reason, saved.fields) });
|
|
215
|
+
return;
|
|
216
|
+
}
|
|
217
|
+
else {
|
|
218
|
+
const revisionId = saved.view.revision.id;
|
|
219
|
+
const preview = await previewRebuild(deps.rebuild, {
|
|
220
|
+
projectId,
|
|
221
|
+
baseRevisionId: revisionId,
|
|
222
|
+
request: { kind: "allAffected" },
|
|
223
|
+
});
|
|
224
|
+
if (!preview.ok) {
|
|
225
|
+
mark({
|
|
226
|
+
state: "failed",
|
|
227
|
+
revisionId,
|
|
228
|
+
heldReason: refused(preview.reason, preview.fields),
|
|
229
|
+
});
|
|
230
|
+
return;
|
|
231
|
+
}
|
|
232
|
+
const cost = covered(db, job, preview.value.costs);
|
|
233
|
+
const waiting = hold(cost);
|
|
234
|
+
if (waiting !== undefined) {
|
|
235
|
+
mark({ state: "held", revisionId, quotedHigh: cost.high, heldReason: waiting });
|
|
236
|
+
return;
|
|
237
|
+
}
|
|
238
|
+
if (!preview.value.work.some((work) => work.disposition === "generate" || work.disposition === "local")) {
|
|
239
|
+
mark({ state: "done", revisionId, heldReason: null });
|
|
240
|
+
return;
|
|
241
|
+
}
|
|
242
|
+
const started = await startRebuild(deps.rebuild, {
|
|
243
|
+
projectId,
|
|
244
|
+
baseRevisionId: revisionId,
|
|
245
|
+
idempotencyKey: deps.start.uuid(),
|
|
246
|
+
previewId: preview.value.id,
|
|
247
|
+
// Accepted on Save: unpriced steps the person's quote covered, or ones they accepted.
|
|
248
|
+
acknowledgeUnknownCosts: cost.unknown === 0 || deps.acks.accepted(job.templateId, job.templateVersion),
|
|
249
|
+
confirmedProvidedWorkKeys: preview.value.providedReuseRequired,
|
|
250
|
+
});
|
|
251
|
+
if (!started.ok) {
|
|
252
|
+
mark({
|
|
253
|
+
state: "failed",
|
|
254
|
+
revisionId,
|
|
255
|
+
heldReason: refused(started.reason, started.fields),
|
|
256
|
+
});
|
|
257
|
+
return;
|
|
258
|
+
}
|
|
259
|
+
mark({ state: "making", revisionId, quotedHigh: cost.high, heldReason: null });
|
|
260
|
+
await finish(deps, job, projectId, revisionId, signal);
|
|
261
|
+
return;
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
// The template's first sample, or one that needs a project of its own.
|
|
266
|
+
const cost = totals(estimateRun(run.draft, run.rendered, words, deps.start.catalogue).rows);
|
|
267
|
+
const waiting = hold(cost);
|
|
268
|
+
if (waiting !== undefined) {
|
|
269
|
+
mark({ state: "held", quotedHigh: cost.high, heldReason: waiting });
|
|
270
|
+
return;
|
|
271
|
+
}
|
|
272
|
+
// Nothing is spent on a provider that isn't set up: Play's own start checks come first.
|
|
273
|
+
const ready = await checkDraftReadiness(deps.start, [run]);
|
|
274
|
+
const missing = ready.fields;
|
|
275
|
+
if (missing.length > 0) {
|
|
276
|
+
mark({
|
|
277
|
+
state: "failed",
|
|
278
|
+
heldReason: `The sample can't start: ${[...new Set(missing.map((field) => field.message))].join(" ")}`,
|
|
279
|
+
});
|
|
280
|
+
return;
|
|
281
|
+
}
|
|
282
|
+
if (replaced !== undefined && !removeHiddenProject(deps.rebuild, replaced.projectId).ok) {
|
|
283
|
+
mark({ state: "failed", heldReason: replaced.sentence });
|
|
284
|
+
return;
|
|
285
|
+
}
|
|
286
|
+
// The sample's row is written before its project, under the id the project will get, so
|
|
287
|
+
// every file the run writes is in the data dir from the very first one.
|
|
288
|
+
const reserved = deps.start.ids.next();
|
|
289
|
+
let given = false;
|
|
290
|
+
const ids = {
|
|
291
|
+
next: () => {
|
|
292
|
+
if (given)
|
|
293
|
+
return deps.start.ids.next();
|
|
294
|
+
given = true;
|
|
295
|
+
return reserved;
|
|
296
|
+
},
|
|
297
|
+
};
|
|
298
|
+
const started = transact(db, () => {
|
|
299
|
+
upsertSample(db, job.templateId, {
|
|
300
|
+
projectId: reserved,
|
|
301
|
+
topic,
|
|
302
|
+
topicSource,
|
|
303
|
+
fullScript,
|
|
304
|
+
at: at(),
|
|
305
|
+
});
|
|
306
|
+
const begun = startRun({ ...deps.start, ids }, run.draft, run.rendered, true, run.templates);
|
|
307
|
+
if (begun.project.id !== reserved)
|
|
308
|
+
throw new Error(internalError("the sample's project got another id than its row", "Press Make sample again"));
|
|
309
|
+
// Paid work is under way on this project from here: a restart continues it as this
|
|
310
|
+
// version's sample (a topic job queued again must not replace it and pay twice).
|
|
311
|
+
continueJobAsSave(db, job.templateId);
|
|
312
|
+
mark({
|
|
313
|
+
state: "making",
|
|
314
|
+
revisionId: currentRevisionId(db, begun.project.id) ?? null,
|
|
315
|
+
quotedHigh: cost.high,
|
|
316
|
+
heldReason: null,
|
|
317
|
+
});
|
|
318
|
+
return begun;
|
|
319
|
+
});
|
|
320
|
+
const newId = started.project.id;
|
|
321
|
+
const revisionId = currentRevisionId(db, newId) ?? null;
|
|
322
|
+
deps.start.runner.tick(newId);
|
|
323
|
+
await finish(deps, job, newId, revisionId, signal);
|
|
324
|
+
}
|
|
325
|
+
// A step the rebuild can only price once the step before it has answered (the narration of an
|
|
326
|
+
// article still to be written) has no price in its preview. The larger of the preview and the
|
|
327
|
+
// price on record is held against what the person saw. The unpriced steps stay unpriced: they
|
|
328
|
+
// start only once the version is covered (`UnknownAcks`), by the person's acceptance or by a
|
|
329
|
+
// quote of theirs that priced them all, never by a price on record alone.
|
|
330
|
+
function covered(db, job, costs) {
|
|
331
|
+
const usesPlan = costs.rows.some((row) => row.onPlan === true);
|
|
332
|
+
const quoted = sampleVersions(db, job.templateId).find((version) => version.templateVersion === job.templateVersion)?.quotedHigh;
|
|
333
|
+
return {
|
|
334
|
+
high: quoted === null || quoted === undefined ? costs.high : Math.max(costs.high, quoted),
|
|
335
|
+
unknown: costs.unknown,
|
|
336
|
+
usesPlan,
|
|
337
|
+
};
|
|
338
|
+
}
|
|
339
|
+
async function finish(deps, job, projectId, revisionId, signal) {
|
|
340
|
+
await settled(deps, projectId, signal);
|
|
341
|
+
const { failed } = outcome(deps.start.db, projectId);
|
|
342
|
+
setSampleVersion(deps.start.db, job.templateId, job.templateVersion, {
|
|
343
|
+
state: failed === null ? "done" : "failed",
|
|
344
|
+
revisionId,
|
|
345
|
+
heldReason: failed,
|
|
346
|
+
at: deps.start.clock.now().toISOString(),
|
|
347
|
+
});
|
|
348
|
+
}
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
import { projectExists } from "../admission/repo.js";
|
|
2
|
+
import { estimateRun } from "../estimate/index.js";
|
|
3
|
+
import { resolvePlayReview } from "../play-drafts/review.js";
|
|
4
|
+
import { templateById } from "../project-templates/repo.js";
|
|
5
|
+
import { budgetRoom, fitsBudget } from "./budget.js";
|
|
6
|
+
import { sampleOf } from "./repo.js";
|
|
7
|
+
import { sampleDocument } from "./scope.js";
|
|
8
|
+
import { affectedSections } from "./sections.js";
|
|
9
|
+
import { defaultSampleTopic } from "./service.js";
|
|
10
|
+
// The sample's single run as Play's review resolves it, on a draft that is never saved.
|
|
11
|
+
export async function resolveSampleRun(review, document, fullScript) {
|
|
12
|
+
const at = review.clock.now().toISOString();
|
|
13
|
+
const view = {
|
|
14
|
+
draft: { id: review.uuid(), version: 1, createdAt: at, updatedAt: at, document },
|
|
15
|
+
attachments: [],
|
|
16
|
+
review: null,
|
|
17
|
+
pendingStart: null,
|
|
18
|
+
start: null,
|
|
19
|
+
};
|
|
20
|
+
const resolved = await resolvePlayReview(review, view, { unsaved: true });
|
|
21
|
+
if (!resolved.ok)
|
|
22
|
+
return {
|
|
23
|
+
ok: false,
|
|
24
|
+
problem: resolved.fields.length === 0
|
|
25
|
+
? "Play's review refused this setup, so no sample can be made. Open the template in Play (Use in Play) to see why."
|
|
26
|
+
: `No sample can be made of this setup: ${[...new Set(resolved.fields.map(fieldSentence))].join(" ")}`,
|
|
27
|
+
};
|
|
28
|
+
const run = resolved.value.runs[0];
|
|
29
|
+
if (run === undefined)
|
|
30
|
+
return {
|
|
31
|
+
ok: false,
|
|
32
|
+
problem: "Play's review found no video to make, so no sample can be made.",
|
|
33
|
+
};
|
|
34
|
+
return {
|
|
35
|
+
ok: true,
|
|
36
|
+
run: { ...run, draft: { ...run.draft, sample: { fullScript } } },
|
|
37
|
+
words: Number(document.expectedWords) || 1500,
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
// A refused field in the studio's words: an empty keyword is named, with where it is filled.
|
|
41
|
+
export function fieldSentence(field) {
|
|
42
|
+
if (field.field.startsWith("values."))
|
|
43
|
+
return `The keyword {{${field.field.slice("values.".length)}}} is empty. Fill it in Subject & research.`;
|
|
44
|
+
return field.message;
|
|
45
|
+
}
|
|
46
|
+
const sectionOfRow = {
|
|
47
|
+
Research: "subject",
|
|
48
|
+
Article: "script",
|
|
49
|
+
"Intro / outro text": "script",
|
|
50
|
+
"Narration descriptions": "voice",
|
|
51
|
+
"Narration Preparation": "voice",
|
|
52
|
+
"Delivery cue overhead": "voice",
|
|
53
|
+
Narration: "voice",
|
|
54
|
+
"Speaker split": "voice",
|
|
55
|
+
"Conversation script": "voice",
|
|
56
|
+
Images: "pictures",
|
|
57
|
+
"Image scenes": "pictures",
|
|
58
|
+
"Establishing image": "pictures",
|
|
59
|
+
Thumbnail: "thumbnail",
|
|
60
|
+
"Thumbnail prompt": "thumbnail",
|
|
61
|
+
"Export / subtitles": "video",
|
|
62
|
+
"Animated images": "video",
|
|
63
|
+
"YouTube description": "upload",
|
|
64
|
+
Posts: "upload",
|
|
65
|
+
Shorts: "shorts",
|
|
66
|
+
Reviews: "reviews",
|
|
67
|
+
Document: "document",
|
|
68
|
+
};
|
|
69
|
+
export function totals(rows) {
|
|
70
|
+
const priced = rows.filter((row) => row.high !== null && row.high > 0);
|
|
71
|
+
return {
|
|
72
|
+
low: rows.reduce((sum, row) => sum + (row.low ?? 0), 0),
|
|
73
|
+
high: rows.reduce((sum, row) => sum + (row.high ?? 0), 0),
|
|
74
|
+
unknown: rows.filter((row) => row.high === null).length,
|
|
75
|
+
onPlan: rows.some((row) => row.onPlan === true) && priced.every((row) => row.onPlan === true),
|
|
76
|
+
usesPlan: rows.some((row) => row.onPlan === true),
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
export async function quoteSave(deps, templateId, document) {
|
|
80
|
+
return quoteSample(deps, templateId, { document });
|
|
81
|
+
}
|
|
82
|
+
// What a sample costs: of a Save (`document`, the sections it outdates), of a new topic (every
|
|
83
|
+
// section) or of the full script switched (the script and what follows it).
|
|
84
|
+
export async function quoteSample(deps, templateId, ask) {
|
|
85
|
+
const template = templateById(deps.db, templateId);
|
|
86
|
+
if (template === undefined)
|
|
87
|
+
return {
|
|
88
|
+
ok: false,
|
|
89
|
+
reason: "not-found",
|
|
90
|
+
message: "This template no longer exists; it may have been deleted. Go to Library → Templates to see your templates.",
|
|
91
|
+
};
|
|
92
|
+
const document = ask.document ?? template.document;
|
|
93
|
+
const sample = sampleOf(deps.db, templateId);
|
|
94
|
+
const fullScript = ask.fullScript ?? sample?.fullScript ?? false;
|
|
95
|
+
const scoped = sampleDocument(document, {
|
|
96
|
+
topic: ask.topic ?? sample?.topic ?? defaultSampleTopic(deps.db),
|
|
97
|
+
fullScript,
|
|
98
|
+
});
|
|
99
|
+
const budget = budgetRoom(deps.db, deps.clock.now(), deps.timeZone);
|
|
100
|
+
const first = ask.sections === "all" || sample === undefined || !projectExists(deps.db, sample.projectId);
|
|
101
|
+
const sections = first
|
|
102
|
+
? []
|
|
103
|
+
: Array.isArray(ask.sections)
|
|
104
|
+
? ask.sections
|
|
105
|
+
: affectedSections(template.document, document).filter((section) => section !== "schedule");
|
|
106
|
+
const resolved = await resolveSampleRun(deps.review, scoped, fullScript);
|
|
107
|
+
if (!resolved.ok)
|
|
108
|
+
return {
|
|
109
|
+
ok: true,
|
|
110
|
+
value: {
|
|
111
|
+
version: template.version,
|
|
112
|
+
first,
|
|
113
|
+
sections,
|
|
114
|
+
rows: [],
|
|
115
|
+
low: 0,
|
|
116
|
+
high: 0,
|
|
117
|
+
unknown: 0,
|
|
118
|
+
onPlan: false,
|
|
119
|
+
usesPlan: false,
|
|
120
|
+
budget,
|
|
121
|
+
fits: false,
|
|
122
|
+
problem: resolved.problem,
|
|
123
|
+
},
|
|
124
|
+
};
|
|
125
|
+
const estimate = estimateRun(resolved.run.draft, resolved.run.rendered, resolved.words, deps.review.catalogue);
|
|
126
|
+
const wanted = new Set(sections);
|
|
127
|
+
const rows = first
|
|
128
|
+
? estimate.rows
|
|
129
|
+
: estimate.rows.filter((row) => {
|
|
130
|
+
const section = sectionOfRow[row.stage];
|
|
131
|
+
// A row the studio can't place is counted: a quote may be high, never low.
|
|
132
|
+
return section === undefined || wanted.has(section);
|
|
133
|
+
});
|
|
134
|
+
const sum = totals(rows);
|
|
135
|
+
return {
|
|
136
|
+
ok: true,
|
|
137
|
+
value: {
|
|
138
|
+
version: template.version,
|
|
139
|
+
first,
|
|
140
|
+
sections,
|
|
141
|
+
rows,
|
|
142
|
+
...sum,
|
|
143
|
+
budget,
|
|
144
|
+
fits: fitsBudget(budget, sum.high, sum.usesPlan),
|
|
145
|
+
problem: null,
|
|
146
|
+
},
|
|
147
|
+
};
|
|
148
|
+
}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { transact } from "../../kernel/db/tx.js";
|
|
3
|
+
import { templateById } from "../project-templates/repo.js";
|
|
4
|
+
import { gateOf } from "./gate.js";
|
|
5
|
+
import { gatesOf, writeGate } from "./repo.js";
|
|
6
|
+
import { getStudio, getStudioChecks } from "./service.js";
|
|
7
|
+
// Mark ready: the newest version passes every free check and every section's sample is
|
|
8
|
+
// current (or off, or provided at run time), so its schedules start running it. Only the
|
|
9
|
+
// newest version is marked; one already ready is answered again without checking.
|
|
10
|
+
export const markReadySchema = z
|
|
11
|
+
.object({ version: z.number().int().positive() })
|
|
12
|
+
.strict()
|
|
13
|
+
.readonly();
|
|
14
|
+
const checkLabel = {
|
|
15
|
+
keywords: "Keywords",
|
|
16
|
+
providers: "Providers",
|
|
17
|
+
voice: "Voice",
|
|
18
|
+
"release-slots": "Release slots",
|
|
19
|
+
"monthly-cost": "Monthly cost",
|
|
20
|
+
answerers: "Checkpoints answered",
|
|
21
|
+
};
|
|
22
|
+
// The section states Mark ready accepts: nothing about them is left to look at.
|
|
23
|
+
const settled = new Set(["current", "off", "provided"]);
|
|
24
|
+
export function readyProblems(checks, sections) {
|
|
25
|
+
return [
|
|
26
|
+
...checks
|
|
27
|
+
.filter((check) => !check.ok)
|
|
28
|
+
.map((check) => ({
|
|
29
|
+
label: checkLabel[check.id],
|
|
30
|
+
sentence: check.sentence,
|
|
31
|
+
...(check.fix === undefined ? {} : { fix: check.fix }),
|
|
32
|
+
})),
|
|
33
|
+
...sections
|
|
34
|
+
.filter((section) => !settled.has(section.state))
|
|
35
|
+
.map((section) => ({
|
|
36
|
+
label: section.label,
|
|
37
|
+
sentence: section.sentence,
|
|
38
|
+
section: section.id,
|
|
39
|
+
})),
|
|
40
|
+
];
|
|
41
|
+
}
|
|
42
|
+
function sentenceOf(problem) {
|
|
43
|
+
const fix = problem.fix === undefined ? "" : ` ${problem.fix.label}.`;
|
|
44
|
+
return `${problem.label}: ${problem.sentence}${fix}`;
|
|
45
|
+
}
|
|
46
|
+
const missingTemplate = "This template no longer exists; it may have been deleted. Go to Library → Templates to see your templates.";
|
|
47
|
+
function notNewest(version, head) {
|
|
48
|
+
return {
|
|
49
|
+
ok: false,
|
|
50
|
+
reason: "conflict",
|
|
51
|
+
message: `Version ${String(version)} is no longer the newest: the template is at version ${String(head)}. Look over version ${String(head)}'s samples, then press Mark ready.`,
|
|
52
|
+
problems: [],
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
export async function markReady(deps, templateId, raw) {
|
|
56
|
+
const parsed = markReadySchema.safeParse(raw);
|
|
57
|
+
if (!parsed.success)
|
|
58
|
+
return {
|
|
59
|
+
ok: false,
|
|
60
|
+
reason: "invalid-input",
|
|
61
|
+
message: "The studio sent a request Slopify couldn't read. Reload the page and try again.",
|
|
62
|
+
problems: [],
|
|
63
|
+
};
|
|
64
|
+
const { version } = parsed.data;
|
|
65
|
+
const template = templateById(deps.db, templateId);
|
|
66
|
+
if (template === undefined)
|
|
67
|
+
return { ok: false, reason: "not-found", message: missingTemplate, problems: [] };
|
|
68
|
+
if (version !== template.version)
|
|
69
|
+
return notNewest(version, template.version);
|
|
70
|
+
if (gateOf(gatesOf(deps.db, templateId), version) === "ready")
|
|
71
|
+
return { ok: true, value: { version, gate: "ready", replayed: true } };
|
|
72
|
+
const [studio, checks] = await Promise.all([
|
|
73
|
+
getStudio(deps, templateId),
|
|
74
|
+
getStudioChecks(deps, templateId),
|
|
75
|
+
]);
|
|
76
|
+
if (!studio.ok || !checks.ok)
|
|
77
|
+
return { ok: false, reason: "not-found", message: missingTemplate, problems: [] };
|
|
78
|
+
// Read while the providers were looked up: a Save meanwhile made a newer version.
|
|
79
|
+
if (studio.value.template.version !== version || checks.value.version !== version)
|
|
80
|
+
return notNewest(version, Math.max(studio.value.template.version, checks.value.version));
|
|
81
|
+
const problems = readyProblems(checks.value.checks, studio.value.sections);
|
|
82
|
+
if (problems.length > 0)
|
|
83
|
+
return {
|
|
84
|
+
ok: false,
|
|
85
|
+
reason: "not-ready",
|
|
86
|
+
message: `Version ${String(version)} can't be marked ready yet. ${problems.map(sentenceOf).join(" ")}`,
|
|
87
|
+
problems,
|
|
88
|
+
};
|
|
89
|
+
return transact(deps.db, () => {
|
|
90
|
+
const head = templateById(deps.db, templateId);
|
|
91
|
+
if (head === undefined)
|
|
92
|
+
return { ok: false, reason: "not-found", message: missingTemplate, problems: [] };
|
|
93
|
+
if (head.version !== version)
|
|
94
|
+
return notNewest(version, head.version);
|
|
95
|
+
writeGate(deps.db, templateId, {
|
|
96
|
+
version,
|
|
97
|
+
state: "ready",
|
|
98
|
+
carriedFrom: null,
|
|
99
|
+
checksJson: JSON.stringify(checks.value.checks),
|
|
100
|
+
markedAt: deps.clock.now().toISOString(),
|
|
101
|
+
});
|
|
102
|
+
return { ok: true, value: { version, gate: "ready", replayed: false } };
|
|
103
|
+
});
|
|
104
|
+
}
|