@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,42 @@
|
|
|
1
|
+
/// <reference lib="esnext.temporal" />
|
|
2
|
+
import { validTimeZone } from "./calendar.js";
|
|
3
|
+
export function rowDate(row, dateField) {
|
|
4
|
+
const value = (row.values[dateField] ?? "").trim();
|
|
5
|
+
if (!/^\d{4}-\d{2}-\d{2}$/.test(value))
|
|
6
|
+
return undefined;
|
|
7
|
+
try {
|
|
8
|
+
return Temporal.PlainDate.from(value, { overflow: "reject" });
|
|
9
|
+
}
|
|
10
|
+
catch {
|
|
11
|
+
return undefined;
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
export function designOccurrences(rows, dateField, daysBefore, time, timeZone, now) {
|
|
15
|
+
if (!validTimeZone(timeZone))
|
|
16
|
+
return [];
|
|
17
|
+
const today = Temporal.Instant.fromEpochMilliseconds(now.valueOf())
|
|
18
|
+
.toZonedDateTimeISO(timeZone)
|
|
19
|
+
.toPlainDate();
|
|
20
|
+
const wanted = Temporal.PlainTime.from(time);
|
|
21
|
+
const found = [];
|
|
22
|
+
for (const [index, row] of rows.entries()) {
|
|
23
|
+
const date = rowDate(row, dateField);
|
|
24
|
+
if (date === undefined)
|
|
25
|
+
continue;
|
|
26
|
+
const over = Temporal.PlainDate.compare(date, today) < 0;
|
|
27
|
+
const planned = date
|
|
28
|
+
.subtract({ days: daysBefore })
|
|
29
|
+
.toPlainDateTime(wanted)
|
|
30
|
+
.toZonedDateTime(timeZone, { disambiguation: "compatible" }).epochMilliseconds;
|
|
31
|
+
found.push({
|
|
32
|
+
at: new Date(over ? now.valueOf() : Math.max(planned, now.valueOf())),
|
|
33
|
+
index,
|
|
34
|
+
over,
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
// Soonest first; rows due at the same moment keep their table order.
|
|
38
|
+
return found.toSorted((a, b) => a.at.valueOf() - b.at.valueOf() || a.index - b.index);
|
|
39
|
+
}
|
|
40
|
+
export function nextDesignOccurrence(rows, dateField, daysBefore, time, timeZone, now) {
|
|
41
|
+
return designOccurrences(rows, dateField, daysBefore, time, timeZone, now)[0] ?? null;
|
|
42
|
+
}
|
|
@@ -12,6 +12,11 @@ export const cadenceSchema = z.discriminatedUnion("kind", [
|
|
|
12
12
|
days: z.array(z.number().int().min(0).max(6)).min(1).max(7).readonly(),
|
|
13
13
|
})
|
|
14
14
|
.strict(),
|
|
15
|
+
// A design schedule: each row of its table is made ahead of the date it holds, at this time
|
|
16
|
+
// (`by-date.ts`). Only design schedules use it, and `nextOccurrence` is never asked about it.
|
|
17
|
+
z
|
|
18
|
+
.object({ kind: z.literal("by-date"), time: z.string().regex(/^([01]\d|2[0-3]):[0-5]\d$/) })
|
|
19
|
+
.strict(),
|
|
15
20
|
]);
|
|
16
21
|
export function validTimeZone(timeZone) {
|
|
17
22
|
try {
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { designOccurrences } from "./by-date.js";
|
|
2
|
+
import { designPlanOf } from "./next-run.js";
|
|
3
|
+
// The calendar's runs of a design schedule: one per row still to make, on the day it is made,
|
|
4
|
+
// named by the row's title. A row whose date is over isn't shown; the scheduler skips it.
|
|
5
|
+
export function upcomingDesignRuns(db, schedule, now, from, to) {
|
|
6
|
+
if (schedule.cadence.kind !== "by-date")
|
|
7
|
+
return [];
|
|
8
|
+
const plan = designPlanOf(db, schedule.templateId);
|
|
9
|
+
if (plan === undefined)
|
|
10
|
+
return [];
|
|
11
|
+
return designOccurrences(schedule.items, plan.dateField, plan.daysBefore, schedule.cadence.time, schedule.timezone, now)
|
|
12
|
+
.filter((one) => !one.over && one.at >= from && one.at < to)
|
|
13
|
+
.map((one) => {
|
|
14
|
+
const row = schedule.items[one.index];
|
|
15
|
+
return {
|
|
16
|
+
at: one.at.toISOString(),
|
|
17
|
+
scheduleId: schedule.id,
|
|
18
|
+
scheduleName: schedule.name,
|
|
19
|
+
scheduleVersion: schedule.version,
|
|
20
|
+
paused: schedule.status === "paused",
|
|
21
|
+
templateId: schedule.templateId,
|
|
22
|
+
templateVersion: plan.readyVersion ?? plan.headVersion,
|
|
23
|
+
templateName: plan.name,
|
|
24
|
+
index: one.index,
|
|
25
|
+
topic: row?.title ?? null,
|
|
26
|
+
topicSource: "queued",
|
|
27
|
+
renderedTitle: row?.title ?? null,
|
|
28
|
+
prepared: null,
|
|
29
|
+
templateKind: "design",
|
|
30
|
+
timezone: schedule.timezone,
|
|
31
|
+
// A design template's readiness is the designs' own (runnableDesignVersion), not the
|
|
32
|
+
// template studio's.
|
|
33
|
+
notReady: false,
|
|
34
|
+
};
|
|
35
|
+
});
|
|
36
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { designFields } from "../designs/fields.js";
|
|
2
|
+
import { getTemplate } from "../designs/templates.js";
|
|
3
|
+
import { rowDate } from "./by-date.js";
|
|
4
|
+
import { designPlanOf } from "./next-run.js";
|
|
5
|
+
export function checkDesign(deps, input) {
|
|
6
|
+
const refuse = (message) => ({ ok: false, reason: "invalid-topics", message });
|
|
7
|
+
if (input.cadence.kind !== "by-date" ||
|
|
8
|
+
input.topicGeneration.mode !== "off" ||
|
|
9
|
+
input.topicKeyword !== null ||
|
|
10
|
+
input.releases.length > 0)
|
|
11
|
+
return {
|
|
12
|
+
ok: false,
|
|
13
|
+
reason: "invalid-input",
|
|
14
|
+
message: "A design schedule makes each row's piece ahead of its date: it has no cadence, topic generation or release days. Choose Ahead of each date.",
|
|
15
|
+
};
|
|
16
|
+
const template = getTemplate(deps.db, input.templateId);
|
|
17
|
+
if (template === undefined)
|
|
18
|
+
return { ok: false, reason: "missing-template" };
|
|
19
|
+
const plan = designPlanOf(deps.db, input.templateId);
|
|
20
|
+
if (plan === undefined)
|
|
21
|
+
return refuse(`The schedule wasn't saved. ${template.name} doesn't say which field holds each date. Open it in Library → Designs → Templates, choose the date field under Schedule, save it, then save the schedule again.`);
|
|
22
|
+
const fields = designFields(plan.document);
|
|
23
|
+
const named = fields.length === 0 ? "none" : fields.map((one) => `{{${one}}}`).join(", ");
|
|
24
|
+
const problems = [];
|
|
25
|
+
for (const keyword of Object.keys(input.values))
|
|
26
|
+
if (!fields.includes(keyword))
|
|
27
|
+
problems.push(`{{${keyword}}} (for every row) is not a field of this template (its fields are ${named}). Remove it.`);
|
|
28
|
+
for (const [index, row] of input.items.entries()) {
|
|
29
|
+
const where = `Row ${String(index + 1)} (${row.title})`;
|
|
30
|
+
for (const keyword of Object.keys(row.values))
|
|
31
|
+
if (!fields.includes(keyword))
|
|
32
|
+
problems.push(`${where} fills {{${keyword}}}, which this template doesn't use (its fields are ${named}). Remove that column.`);
|
|
33
|
+
if (rowDate(row, plan.dateField) === undefined)
|
|
34
|
+
problems.push(`${where} has no date in {{${plan.dateField}}}. Write it as YYYY-MM-DD, for example 2026-11-14.`);
|
|
35
|
+
}
|
|
36
|
+
if (problems.length === 0)
|
|
37
|
+
return { ok: true, value: template.headVersion };
|
|
38
|
+
const shown = problems.slice(0, 5);
|
|
39
|
+
const more = problems.length - shown.length;
|
|
40
|
+
return refuse(`The schedule wasn't saved. ${shown.join(" ")}${more > 0 ? ` And ${String(more)} more like these.` : ""}`);
|
|
41
|
+
}
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
/// <reference lib="esnext.temporal" />
|
|
2
|
+
import { transact } from "../../kernel/db/tx.js";
|
|
3
|
+
import { missingValues } from "../designs/fields.js";
|
|
4
|
+
import { makeFromTemplate, quoteDocument } from "../designs/template-make.js";
|
|
5
|
+
import { designOccurrences } from "./by-date.js";
|
|
6
|
+
import { nextOccurrence } from "./calendar.js";
|
|
7
|
+
import { designPlanOf, missedGraceMs, nextRunOf } from "./next-run.js";
|
|
8
|
+
import { insertRun, scheduleById, updateRun } from "./repo.js";
|
|
9
|
+
// One due run of a design schedule, all at once and in one transaction: it takes the row due
|
|
10
|
+
// soonest, makes its piece from the template's ready version and starts the artwork (the only
|
|
11
|
+
// paid step, made in the background by the design runner), removes the row and records the
|
|
12
|
+
// design on the run. Nothing here waits on a provider, so the tick moves on at once.
|
|
13
|
+
//
|
|
14
|
+
// A row whose date is over is removed with a skipped run that says so. A run that can't start
|
|
15
|
+
// (no ready version, a value missing, over the spend limit, no image provider) keeps its row,
|
|
16
|
+
// records why, and tries again at the same time the next day; marking a version ready tries
|
|
17
|
+
// again at once (`refreshDesignSchedules`). None of this reaches needs_attention: it shows on
|
|
18
|
+
// the schedule's runs.
|
|
19
|
+
export function claimDesignRun(deps, schedule, now) {
|
|
20
|
+
const designs = deps.designs?.();
|
|
21
|
+
transact(deps.db, () => {
|
|
22
|
+
const current = scheduleById(deps.db, schedule.id);
|
|
23
|
+
if (current === undefined ||
|
|
24
|
+
current.version !== schedule.version ||
|
|
25
|
+
current.nextRunAt !== schedule.nextRunAt ||
|
|
26
|
+
current.status !== "active" ||
|
|
27
|
+
current.cadence.kind !== "by-date")
|
|
28
|
+
return;
|
|
29
|
+
const time = current.cadence.time;
|
|
30
|
+
const plan = designPlanOf(deps.db, current.templateId);
|
|
31
|
+
const due = plan === undefined
|
|
32
|
+
? undefined
|
|
33
|
+
: designOccurrences(current.items, plan.dateField, plan.daysBefore, time, current.timezone, now)[0];
|
|
34
|
+
// Rows edited meanwhile: nothing is due yet, so only the next run moves.
|
|
35
|
+
if (plan === undefined || due === undefined || due.at > now) {
|
|
36
|
+
setNext(deps, current, nextRunOf(deps.db, current, now), current.items, now);
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
const row = current.items[due.index];
|
|
40
|
+
if (row === undefined)
|
|
41
|
+
return;
|
|
42
|
+
const run = (status, error) => ({
|
|
43
|
+
id: deps.uuid(),
|
|
44
|
+
scheduleId: current.id,
|
|
45
|
+
scheduledFor: schedule.nextRunAt ?? now.toISOString(),
|
|
46
|
+
status,
|
|
47
|
+
requestId: null,
|
|
48
|
+
projectIds: [],
|
|
49
|
+
designIds: [],
|
|
50
|
+
estimate: null,
|
|
51
|
+
startedAt: now.toISOString(),
|
|
52
|
+
endedAt: status === "running" ? null : now.toISOString(),
|
|
53
|
+
error,
|
|
54
|
+
});
|
|
55
|
+
const without = current.items.filter((_item, index) => index !== due.index);
|
|
56
|
+
const date = row.values[plan.dateField] ?? "";
|
|
57
|
+
if (due.over) {
|
|
58
|
+
insertRun(deps.db, run("skipped", `Skipped: ${row.title} on ${date} is already over.`));
|
|
59
|
+
setNext(deps, current, nextRunOf(deps.db, { ...current, items: without }, now), without, now);
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
const tomorrow = () => {
|
|
63
|
+
const next = nextOccurrence({ kind: "daily", time }, current.timezone, now);
|
|
64
|
+
const soonest = nextRunOf(deps.db, current, new Date(now.valueOf() + 1000));
|
|
65
|
+
return next === null ? soonest : soonest === null || soonest < next ? next : soonest;
|
|
66
|
+
};
|
|
67
|
+
const keep = (status, error) => {
|
|
68
|
+
insertRun(deps.db, run(status, error));
|
|
69
|
+
setNext(deps, current, tomorrow(), current.items, now);
|
|
70
|
+
};
|
|
71
|
+
// A run Slopify wasn't open for, like a project schedule's: skipped when the schedule skips
|
|
72
|
+
// missed runs. The row stays and is tried again at the same time the next day.
|
|
73
|
+
const scheduledFor = schedule.nextRunAt === null ? now : new Date(schedule.nextRunAt);
|
|
74
|
+
if (current.missedPolicy === "skip" && now.valueOf() - scheduledFor.valueOf() > missedGraceMs)
|
|
75
|
+
return keep("skipped", `Skipped: Slopify wasn't open at ${time} when ${row.title}'s piece was due, and this schedule skips the runs it misses. It tries again at ${time} tomorrow; to make a missed piece as soon as Slopify opens, set Missed run to Run once when Slopify reopens in Library → Designs → Templates → ${plan.name} → Schedule.`);
|
|
76
|
+
if (designs === undefined)
|
|
77
|
+
return keep("failed", "Not started: designs aren't available in this copy of Slopify. Restart Slopify; if it keeps happening, reinstall it.");
|
|
78
|
+
if (plan.readyVersion === null)
|
|
79
|
+
return keep("skipped", `Not started: ${plan.name} has no version marked ready. Open it in Library → Designs → Templates and press Mark ready.`);
|
|
80
|
+
const missing = missingValues(plan.document, row.values);
|
|
81
|
+
if (missing.length > 0) {
|
|
82
|
+
const list = missing.map((name) => `{{${name}}}`).join(", ");
|
|
83
|
+
return keep("failed", `Not started: ${row.title} has no ${list} in its row. Fill ${missing.length === 1 ? "it" : "them"} in under Schedules → ${current.name}; the next try is at ${time}.`);
|
|
84
|
+
}
|
|
85
|
+
const quote = quoteDocument(designs.catalogue(), plan.document);
|
|
86
|
+
if (current.spendLimitCents !== null &&
|
|
87
|
+
(quote.unknown > 0 || quote.high * 100 > current.spendLimitCents))
|
|
88
|
+
return keep("failed", `Not started: ${row.title}'s piece costs up to $${quote.high.toFixed(2)}${quote.unknown > 0 ? " and some prices are unknown" : ""}, over this schedule's spend limit. Raise the spend limit or choose an image model with a known, lower price.`);
|
|
89
|
+
const started = run("running", null);
|
|
90
|
+
if (!insertRun(deps.db, started))
|
|
91
|
+
return;
|
|
92
|
+
const made = makeFromTemplate(designs, current.templateId, {
|
|
93
|
+
mutationId: `schedule-${started.id}`,
|
|
94
|
+
values: row.values,
|
|
95
|
+
acknowledgedHigh: quote.high,
|
|
96
|
+
// A schedule with no spend limit makes pieces whose price is unknown, as it always has.
|
|
97
|
+
acknowledgedUnknown: quote.unknown > 0,
|
|
98
|
+
version: plan.readyVersion,
|
|
99
|
+
name: row.title,
|
|
100
|
+
}, { scheduleId: current.id, scheduleRunId: started.id });
|
|
101
|
+
if (!made.ok) {
|
|
102
|
+
updateRun(deps.db, {
|
|
103
|
+
...started,
|
|
104
|
+
status: "failed",
|
|
105
|
+
endedAt: now.toISOString(),
|
|
106
|
+
error: `Not started: ${made.message}`,
|
|
107
|
+
});
|
|
108
|
+
setNext(deps, current, tomorrow(), current.items, now);
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
updateRun(deps.db, {
|
|
112
|
+
...started,
|
|
113
|
+
status: "succeeded",
|
|
114
|
+
designIds: [made.value.id],
|
|
115
|
+
estimate: [quote],
|
|
116
|
+
endedAt: now.toISOString(),
|
|
117
|
+
});
|
|
118
|
+
setNext(deps, current, nextRunOf(deps.db, { ...current, items: without }, now), without, now);
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
// A design schedule stays active when its table empties: rows added later run as they come.
|
|
122
|
+
function setNext(deps, schedule, next, items, now) {
|
|
123
|
+
deps.db
|
|
124
|
+
.prepare("UPDATE schedules SET items_json=?, next_run_at=?, version=version+1, updated_at=? WHERE id=? AND version=?")
|
|
125
|
+
.run(JSON.stringify(items), next?.toISOString() ?? null, now.toISOString(), schedule.id, schedule.version);
|
|
126
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { runnableDesignVersion } from "../designs/gate.js";
|
|
2
|
+
import { getTemplate } from "../designs/templates.js";
|
|
3
|
+
import { nextDesignOccurrence } from "./by-date.js";
|
|
4
|
+
import { nextOccurrence } from "./calendar.js";
|
|
5
|
+
// How late a run may start and still count as on time; later, it was missed (Slopify was
|
|
6
|
+
// closed), and a schedule that skips missed runs skips it.
|
|
7
|
+
export const missedGraceMs = 60_000;
|
|
8
|
+
export function designPlanOf(db, templateId) {
|
|
9
|
+
const template = getTemplate(db, templateId);
|
|
10
|
+
if (template === undefined)
|
|
11
|
+
return undefined;
|
|
12
|
+
const ready = runnableDesignVersion(db, templateId);
|
|
13
|
+
const document = ready?.document ?? template.document;
|
|
14
|
+
const schedule = document.schedule;
|
|
15
|
+
if (schedule === undefined)
|
|
16
|
+
return undefined;
|
|
17
|
+
return {
|
|
18
|
+
name: ready?.name ?? template.name,
|
|
19
|
+
dateField: schedule.dateField,
|
|
20
|
+
daysBefore: schedule.daysBefore,
|
|
21
|
+
readyVersion: ready?.version ?? null,
|
|
22
|
+
headVersion: template.headVersion,
|
|
23
|
+
document,
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
export function nextRunOf(db, schedule, now) {
|
|
27
|
+
if (schedule.cadence.kind !== "by-date")
|
|
28
|
+
return nextOccurrence(schedule.cadence, schedule.timezone, now);
|
|
29
|
+
const plan = designPlanOf(db, schedule.templateId);
|
|
30
|
+
if (plan === undefined)
|
|
31
|
+
return null;
|
|
32
|
+
return (nextDesignOccurrence(schedule.items, plan.dateField, plan.daysBefore, schedule.cadence.time, schedule.timezone, now)?.at ?? null);
|
|
33
|
+
}
|
|
34
|
+
// Every active design schedule of a template takes its next run again, for when a version is
|
|
35
|
+
// marked ready or the template's dates change.
|
|
36
|
+
export function refreshDesignSchedules(db, templateId, now) {
|
|
37
|
+
const rows = db
|
|
38
|
+
.prepare(`SELECT id FROM schedules WHERE template_kind='design' AND template_id=? AND status='active' AND deleted_at IS NULL`)
|
|
39
|
+
.all(templateId)
|
|
40
|
+
.map((row) => String(row.id));
|
|
41
|
+
let changed = 0;
|
|
42
|
+
for (const id of rows) {
|
|
43
|
+
const raw = db
|
|
44
|
+
.prepare("SELECT cadence_json, timezone, items_json, next_run_at FROM schedules WHERE id=?")
|
|
45
|
+
.get(id);
|
|
46
|
+
if (raw === undefined)
|
|
47
|
+
continue;
|
|
48
|
+
const next = nextRunOf(db, {
|
|
49
|
+
templateKind: "design",
|
|
50
|
+
templateId,
|
|
51
|
+
timezone: String(raw.timezone),
|
|
52
|
+
cadence: JSON.parse(String(raw.cadence_json)),
|
|
53
|
+
items: JSON.parse(String(raw.items_json)),
|
|
54
|
+
}, now);
|
|
55
|
+
const at = next?.toISOString() ?? null;
|
|
56
|
+
if (at === raw.next_run_at)
|
|
57
|
+
continue;
|
|
58
|
+
db.prepare("UPDATE schedules SET next_run_at=?, version=version+1, updated_at=? WHERE id=?").run(at, now.toISOString(), id);
|
|
59
|
+
changed += 1;
|
|
60
|
+
}
|
|
61
|
+
return changed;
|
|
62
|
+
}
|
|
@@ -8,6 +8,7 @@ import { createDraft } from "../play-drafts/service.js";
|
|
|
8
8
|
import { startPlayDraft } from "../play-drafts/start.js";
|
|
9
9
|
import { freshTemplateDraft } from "../project-templates/setup.js";
|
|
10
10
|
import { currentRevisionId } from "../revisions/repo.js";
|
|
11
|
+
import { notTemplateSample } from "../template-studio/repo.js";
|
|
11
12
|
import { scheduleById } from "./repo.js";
|
|
12
13
|
import { renderedTitle, scheduledValues } from "./topic-list.js";
|
|
13
14
|
// The draft a schedule's run starts for one topic: the template as it is now, the topic in its
|
|
@@ -43,9 +44,19 @@ export async function prepareTopic(deps, input) {
|
|
|
43
44
|
const schedule = scheduleById(deps.db, input.scheduleId);
|
|
44
45
|
if (schedule === undefined)
|
|
45
46
|
return { ok: false, reason: "not-found" };
|
|
47
|
+
// A design schedule's piece is made on its day; there is no video to hold back.
|
|
48
|
+
if (schedule.templateKind === "design")
|
|
49
|
+
return { ok: false, reason: "design" };
|
|
50
|
+
// A paused schedule makes nothing, ahead or on its day.
|
|
51
|
+
if (schedule.status === "paused")
|
|
52
|
+
return { ok: false, reason: "paused" };
|
|
46
53
|
const topic = schedule.items.find((item) => item.title === input.title);
|
|
47
54
|
if (topic === undefined)
|
|
48
55
|
return { ok: false, reason: "not-found" };
|
|
56
|
+
// Prepared ahead is the schedule's run made early: it waits for a ready version too.
|
|
57
|
+
const notReady = deps.notReady?.(schedule.templateId);
|
|
58
|
+
if (notReady !== undefined)
|
|
59
|
+
return { ok: false, reason: "not-ready", detail: notReady };
|
|
49
60
|
const document = scheduledDocument(deps, schedule, topic);
|
|
50
61
|
if (document === undefined)
|
|
51
62
|
return { ok: false, reason: "missing-template" };
|
|
@@ -104,9 +115,13 @@ const ended = new Set(["done", "partial", "failed", "canceled"]);
|
|
|
104
115
|
// run the same title, so only a prepared project counts there.
|
|
105
116
|
export function preparedProject(db, scheduleId, title, byTitle = true) {
|
|
106
117
|
const live = "project_id NOT IN (SELECT project_id FROM project_trash)";
|
|
118
|
+
// A template studio sample is titled from the template's next queued topic, so it can have
|
|
119
|
+
// the run's title; it is the studio's alone and never the run's to continue.
|
|
107
120
|
const prepared = db
|
|
108
121
|
.prepare(`SELECT project_id, added_checkpoint FROM prepared_videos
|
|
109
|
-
WHERE schedule_id=? AND title=? AND ${live}
|
|
122
|
+
WHERE schedule_id=? AND title=? AND ${live}
|
|
123
|
+
AND ${notTemplateSample("prepared_videos", "project_id")}
|
|
124
|
+
ORDER BY prepared_at DESC LIMIT 1`)
|
|
110
125
|
.get(scheduleId, title);
|
|
111
126
|
// A prepared project canceled since is no video to continue: the day makes a new one.
|
|
112
127
|
if (typeof prepared?.project_id === "string" &&
|
|
@@ -115,7 +130,8 @@ export function preparedProject(db, scheduleId, title, byTitle = true) {
|
|
|
115
130
|
if (!byTitle)
|
|
116
131
|
return undefined;
|
|
117
132
|
for (const row of db
|
|
118
|
-
.prepare(
|
|
133
|
+
.prepare(`SELECT id FROM projects WHERE title=? AND id NOT IN (SELECT project_id FROM project_trash)
|
|
134
|
+
AND ${notTemplateSample()} ORDER BY created_at DESC`)
|
|
119
135
|
.all(title)) {
|
|
120
136
|
const id = String(row.id);
|
|
121
137
|
if (!ended.has(derive(stagesOf(db, id), projectPaused(db, id))))
|
|
@@ -8,6 +8,7 @@ import { scheduleRunSchema, scheduleSummarySchema, } from "./schema.js";
|
|
|
8
8
|
const rowSchema = z.object({
|
|
9
9
|
id: z.string(),
|
|
10
10
|
name: z.string(),
|
|
11
|
+
template_kind: z.string(),
|
|
11
12
|
template_id: z.string(),
|
|
12
13
|
template_version: z.number(),
|
|
13
14
|
cadence_json: z.string(),
|
|
@@ -33,12 +34,13 @@ const rowSchema = z.object({
|
|
|
33
34
|
topics_failed_at: z.string().nullable(),
|
|
34
35
|
topics_error: z.string().nullable(),
|
|
35
36
|
releases_json: z.string().nullable(),
|
|
37
|
+
topic_scoring: z.string(),
|
|
36
38
|
held_topics: z.number(),
|
|
37
39
|
});
|
|
38
|
-
const scheduleColumns = `id,name,template_id,template_version,cadence_json,timezone,missed_policy,
|
|
40
|
+
const scheduleColumns = `id,name,template_kind,template_id,template_version,cadence_json,timezone,missed_policy,
|
|
39
41
|
overlap_policy,spend_limit_cents,items_json,topic_keyword,values_json,status,version,next_run_at,
|
|
40
42
|
created_at,updated_at,deleted_at,brief,topic_mode,topic_min,topic_llm_json,topics_generating_at,
|
|
41
|
-
topics_generated_at,topics_failed_at,topics_error,releases_json,
|
|
43
|
+
topics_generated_at,topics_failed_at,topics_error,releases_json,topic_scoring,
|
|
42
44
|
(SELECT count(*) FROM schedule_topics AS held
|
|
43
45
|
WHERE held.schedule_id=schedules.id AND held.state='held') AS held_topics`;
|
|
44
46
|
export function scheduleById(db, id) {
|
|
@@ -66,11 +68,11 @@ export function dueSchedules(db, now) {
|
|
|
66
68
|
}
|
|
67
69
|
export function insertSchedule(db, schedule, creationHash) {
|
|
68
70
|
db.prepare(`INSERT INTO schedules
|
|
69
|
-
(id,name,template_id,template_version,cadence_json,timezone,missed_policy,
|
|
70
|
-
spend_limit_cents,items_json,topic_keyword,values_json,status,version,
|
|
71
|
-
next_run_at,created_at,updated_at,deleted_at,brief,topic_mode,topic_min,
|
|
72
|
-
releases_json)
|
|
73
|
-
VALUES (
|
|
71
|
+
(id,name,template_kind,template_id,template_version,cadence_json,timezone,missed_policy,
|
|
72
|
+
overlap_policy,spend_limit_cents,items_json,topic_keyword,values_json,status,version,
|
|
73
|
+
creation_hash,next_run_at,created_at,updated_at,deleted_at,brief,topic_mode,topic_min,
|
|
74
|
+
topic_llm_json,releases_json,topic_scoring)
|
|
75
|
+
VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)`).run(schedule.id, schedule.name, schedule.templateKind, schedule.templateId, schedule.templateVersion, JSON.stringify(schedule.cadence), schedule.timezone, schedule.missedPolicy, schedule.overlapPolicy, schedule.spendLimitCents, JSON.stringify(schedule.items), schedule.topicKeyword, JSON.stringify(schedule.values), schedule.status, schedule.version, creationHash, schedule.nextRunAt, schedule.createdAt, schedule.updatedAt, schedule.deletedAt, ...generationColumns(schedule));
|
|
74
76
|
}
|
|
75
77
|
function generationColumns(schedule) {
|
|
76
78
|
const generation = schedule.topicGeneration;
|
|
@@ -80,18 +82,20 @@ function generationColumns(schedule) {
|
|
|
80
82
|
generation.keepAtLeast,
|
|
81
83
|
generation.llm === null ? null : JSON.stringify(generation.llm),
|
|
82
84
|
schedule.releases.length === 0 ? null : JSON.stringify(schedule.releases),
|
|
85
|
+
schedule.topicScoring,
|
|
83
86
|
];
|
|
84
87
|
}
|
|
85
88
|
export function updateScheduleRow(db, schedule, mutationId, mutationHash, baseVersion) {
|
|
86
89
|
const changed = db
|
|
87
|
-
.prepare(`UPDATE schedules SET name=?,template_id=?,template_version=?,cadence_json=?,timezone=?,
|
|
90
|
+
.prepare(`UPDATE schedules SET name=?,template_kind=?,template_id=?,template_version=?,cadence_json=?,timezone=?,
|
|
88
91
|
missed_policy=?,overlap_policy=?,spend_limit_cents=?,items_json=?,topic_keyword=?,
|
|
89
92
|
values_json=?,status=?,version=?,
|
|
90
93
|
next_run_at=?,updated_at=?,mutation_id=?,mutation_hash=?,
|
|
91
|
-
brief=?,topic_mode=?,topic_min=?,topic_llm_json=?,releases_json=?,
|
|
94
|
+
brief=?,topic_mode=?,topic_min=?,topic_llm_json=?,releases_json=?,topic_scoring=?,
|
|
95
|
+
topics_failed_at=NULL,
|
|
92
96
|
topics_error=NULL
|
|
93
97
|
WHERE id=? AND version=? AND deleted_at IS NULL`)
|
|
94
|
-
.run(schedule.name, schedule.templateId, schedule.templateVersion, JSON.stringify(schedule.cadence), schedule.timezone, schedule.missedPolicy, schedule.overlapPolicy, schedule.spendLimitCents, JSON.stringify(schedule.items), schedule.topicKeyword, JSON.stringify(schedule.values), schedule.status, schedule.version, schedule.nextRunAt, schedule.updatedAt, mutationId, mutationHash, ...generationColumns(schedule), schedule.id, baseVersion);
|
|
98
|
+
.run(schedule.name, schedule.templateKind, schedule.templateId, schedule.templateVersion, JSON.stringify(schedule.cadence), schedule.timezone, schedule.missedPolicy, schedule.overlapPolicy, schedule.spendLimitCents, JSON.stringify(schedule.items), schedule.topicKeyword, JSON.stringify(schedule.values), schedule.status, schedule.version, schedule.nextRunAt, schedule.updatedAt, mutationId, mutationHash, ...generationColumns(schedule), schedule.id, baseVersion);
|
|
95
99
|
return Number(changed.changes) === 1;
|
|
96
100
|
}
|
|
97
101
|
export function tombstoneScheduleRow(db, id, version, deletedAt) {
|
|
@@ -111,8 +115,8 @@ export function setScheduleStatus(db, id, version, status, nextRunAt, updatedAt)
|
|
|
111
115
|
export function insertRun(db, run) {
|
|
112
116
|
try {
|
|
113
117
|
db.prepare(`INSERT INTO schedule_runs
|
|
114
|
-
(id,schedule_id,scheduled_for,status,request_id,project_ids_json,estimate_json,started_at,ended_at,error)
|
|
115
|
-
VALUES (
|
|
118
|
+
(id,schedule_id,scheduled_for,status,request_id,project_ids_json,design_ids_json,estimate_json,started_at,ended_at,error)
|
|
119
|
+
VALUES (?,?,?,?,?,?,?,?,?,?,?)`).run(run.id, run.scheduleId, run.scheduledFor, run.status, run.requestId, JSON.stringify(run.projectIds), JSON.stringify(run.designIds), run.estimate === null ? null : JSON.stringify(run.estimate), run.startedAt, run.endedAt, run.error);
|
|
116
120
|
return true;
|
|
117
121
|
}
|
|
118
122
|
catch (error) {
|
|
@@ -122,8 +126,8 @@ export function insertRun(db, run) {
|
|
|
122
126
|
}
|
|
123
127
|
}
|
|
124
128
|
export function updateRun(db, run) {
|
|
125
|
-
db.prepare(`UPDATE schedule_runs SET status=?,request_id=?,project_ids_json=?,
|
|
126
|
-
ended_at=?,error=? WHERE id=?`).run(run.status, run.requestId, JSON.stringify(run.projectIds), run.estimate === null ? null : JSON.stringify(run.estimate), run.endedAt, run.error, run.id);
|
|
129
|
+
db.prepare(`UPDATE schedule_runs SET status=?,request_id=?,project_ids_json=?,design_ids_json=?,
|
|
130
|
+
estimate_json=?,ended_at=?,error=? WHERE id=?`).run(run.status, run.requestId, JSON.stringify(run.projectIds), JSON.stringify(run.designIds), run.estimate === null ? null : JSON.stringify(run.estimate), run.endedAt, run.error, run.id);
|
|
127
131
|
}
|
|
128
132
|
export function recordRunStartIdentity(db, run) {
|
|
129
133
|
if (run.requestId === null)
|
|
@@ -174,8 +178,8 @@ function consumeTopic(db, scheduleId, used, updatedAt) {
|
|
|
174
178
|
}
|
|
175
179
|
export function runsForSchedule(db, scheduleId) {
|
|
176
180
|
return db
|
|
177
|
-
.prepare(`SELECT id,schedule_id,scheduled_for,status,request_id,project_ids_json,
|
|
178
|
-
started_at,ended_at,error FROM schedule_runs WHERE schedule_id=? ORDER BY started_at DESC,id DESC`)
|
|
181
|
+
.prepare(`SELECT id,schedule_id,scheduled_for,status,request_id,project_ids_json,design_ids_json,
|
|
182
|
+
estimate_json,started_at,ended_at,error FROM schedule_runs WHERE schedule_id=? ORDER BY started_at DESC,id DESC`)
|
|
179
183
|
.all(scheduleId)
|
|
180
184
|
.map(parseRun);
|
|
181
185
|
}
|
|
@@ -288,6 +292,7 @@ function parseSchedule(row) {
|
|
|
288
292
|
return scheduleSummarySchema.parse({
|
|
289
293
|
id: value.id,
|
|
290
294
|
name: value.name,
|
|
295
|
+
templateKind: value.template_kind,
|
|
291
296
|
templateId: value.template_id,
|
|
292
297
|
templateVersion: value.template_version,
|
|
293
298
|
cadence: JSON.parse(value.cadence_json),
|
|
@@ -305,6 +310,7 @@ function parseSchedule(row) {
|
|
|
305
310
|
llm: value.topic_llm_json === null ? null : JSON.parse(value.topic_llm_json),
|
|
306
311
|
},
|
|
307
312
|
releases: value.releases_json === null ? [] : JSON.parse(value.releases_json),
|
|
313
|
+
topicScoring: value.topic_scoring,
|
|
308
314
|
topics: {
|
|
309
315
|
held: value.held_topics,
|
|
310
316
|
generatingSince: value.topics_generating_at,
|
|
@@ -329,6 +335,7 @@ function parseRun(row) {
|
|
|
329
335
|
status: z.string(),
|
|
330
336
|
request_id: z.string().nullable(),
|
|
331
337
|
project_ids_json: z.string(),
|
|
338
|
+
design_ids_json: z.string(),
|
|
332
339
|
estimate_json: z.string().nullable(),
|
|
333
340
|
started_at: z.string(),
|
|
334
341
|
ended_at: z.string().nullable(),
|
|
@@ -342,6 +349,7 @@ function parseRun(row) {
|
|
|
342
349
|
status: value.status,
|
|
343
350
|
requestId: value.request_id,
|
|
344
351
|
projectIds: JSON.parse(value.project_ids_json),
|
|
352
|
+
designIds: JSON.parse(value.design_ids_json),
|
|
345
353
|
estimate: value.estimate_json === null ? null : JSON.parse(value.estimate_json),
|
|
346
354
|
startedAt: value.started_at,
|
|
347
355
|
endedAt: value.ended_at,
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { transact } from "../../kernel/db/tx.js";
|
|
2
|
+
import { scheduleById } from "./repo.js";
|
|
3
|
+
import { rankedItems, saveScores, scoreTopics } from "./topic-scores.js";
|
|
4
|
+
import { heldTopics, knownTitles, rankHeld } from "./topics.js";
|
|
5
|
+
// Rescore: every queued and held topic scored again now, then the queue and the held list put
|
|
6
|
+
// in score order. Topics the person added or moved keep their places: one scored for the first
|
|
7
|
+
// time here is scored where it stands.
|
|
8
|
+
export async function rescoreTopics(deps, scheduleId, signal) {
|
|
9
|
+
const schedule = scheduleById(deps.db, scheduleId);
|
|
10
|
+
if (schedule === undefined || schedule.deletedAt !== null)
|
|
11
|
+
return { ok: false, reason: "not-found" };
|
|
12
|
+
if (schedule.topicScoring === "off")
|
|
13
|
+
return {
|
|
14
|
+
ok: false,
|
|
15
|
+
reason: "scoring-off",
|
|
16
|
+
message: "Topic ranking is off for this schedule. Choose how to rank topics under Edit → Topic generation → Rank topics, save, then rescore.",
|
|
17
|
+
};
|
|
18
|
+
const titles = [
|
|
19
|
+
...schedule.items.map((item) => item.title),
|
|
20
|
+
...heldTopics(deps, scheduleId).map((topic) => topic.title),
|
|
21
|
+
];
|
|
22
|
+
const scores = await scoreTopics(deps, schedule.topicScoring, titles, knownTitles(deps, scheduleId), deps.template(schedule.templateId)?.document.form.language ?? "en", signal);
|
|
23
|
+
return transact(deps.db, () => {
|
|
24
|
+
const current = scheduleById(deps.db, scheduleId);
|
|
25
|
+
if (current === undefined || current.deletedAt !== null)
|
|
26
|
+
return { ok: false, reason: "not-found" };
|
|
27
|
+
const at = deps.clock.now().toISOString();
|
|
28
|
+
saveScores(deps.db, scheduleId, scores, at, true);
|
|
29
|
+
deps.db
|
|
30
|
+
.prepare("UPDATE schedules SET items_json=?,version=version+1,updated_at=? WHERE id=? AND version=?")
|
|
31
|
+
.run(JSON.stringify(rankedItems(deps.db, scheduleId, current.items)), at, scheduleId, current.version);
|
|
32
|
+
rankHeld(deps, scheduleId);
|
|
33
|
+
return { ok: true, value: scheduleById(deps.db, scheduleId) };
|
|
34
|
+
});
|
|
35
|
+
}
|
|
@@ -3,10 +3,11 @@ import { reviewDraft } from "../play-drafts/review.js";
|
|
|
3
3
|
import { createDraft } from "../play-drafts/service.js";
|
|
4
4
|
import { startPlayDraft } from "../play-drafts/start.js";
|
|
5
5
|
import { nextOccurrence } from "./calendar.js";
|
|
6
|
+
import { claimDesignRun } from "./design-run.js";
|
|
7
|
+
import { missedGraceMs } from "./next-run.js";
|
|
6
8
|
import { continuePrepared, preparedProject, scheduledDocument, scheduledTitle } from "./prepare.js";
|
|
7
9
|
import { activeRun, dueSchedules, insertRun, recordRunDispatch, recordRunStartIdentity, recoverRunningRuns, scheduleById, scheduleRows, updateRun, } from "./repo.js";
|
|
8
10
|
import { generateTopics, generationDue, releaseTopicLeases } from "./topics.js";
|
|
9
|
-
const missedGraceMs = 60_000;
|
|
10
11
|
export function createScheduleRunner(deps) {
|
|
11
12
|
let ticking = false;
|
|
12
13
|
// ceiling: one entry per schedule generating now.
|
|
@@ -76,6 +77,11 @@ export function createScheduleRunner(deps) {
|
|
|
76
77
|
function claim(deps, schedule, now) {
|
|
77
78
|
if (schedule.nextRunAt === null)
|
|
78
79
|
return [];
|
|
80
|
+
// A design schedule's run makes its piece at once and never reaches `execute`.
|
|
81
|
+
if (schedule.templateKind === "design") {
|
|
82
|
+
claimDesignRun(deps, schedule, now);
|
|
83
|
+
return [];
|
|
84
|
+
}
|
|
79
85
|
const scheduledFor = new Date(schedule.nextRunAt);
|
|
80
86
|
const occurrence = schedule.missedPolicy === "skip" && now.valueOf() - scheduledFor.valueOf() > missedGraceMs;
|
|
81
87
|
let next = nextOccurrence(schedule.cadence, schedule.timezone, new Date(scheduledFor.valueOf() + 1000));
|
|
@@ -94,7 +100,10 @@ function claim(deps, schedule, now) {
|
|
|
94
100
|
const noTopic = current.topicGeneration.mode !== "off" && current.items.length === 0
|
|
95
101
|
? noTopicReason(current)
|
|
96
102
|
: null;
|
|
97
|
-
|
|
103
|
+
// A template with no version marked ready (or saved before the studio) waits: the run is
|
|
104
|
+
// skipped, nothing is spent and no topic is used.
|
|
105
|
+
const notReady = noTopic === null ? (deps.notReady?.(current.templateId) ?? null) : null;
|
|
106
|
+
const skip = occurrence || overlapping || noTopic !== null || notReady !== null;
|
|
98
107
|
const run = {
|
|
99
108
|
id: deps.uuid(),
|
|
100
109
|
scheduleId: schedule.id,
|
|
@@ -102,6 +111,7 @@ function claim(deps, schedule, now) {
|
|
|
102
111
|
status: skip ? "skipped" : "running",
|
|
103
112
|
requestId: null,
|
|
104
113
|
projectIds: [],
|
|
114
|
+
designIds: [],
|
|
105
115
|
estimate: null,
|
|
106
116
|
startedAt: now.toISOString(),
|
|
107
117
|
endedAt: skip ? now.toISOString() : null,
|
|
@@ -110,7 +120,7 @@ function claim(deps, schedule, now) {
|
|
|
110
120
|
? "Skipped because the previous run of this schedule was still going."
|
|
111
121
|
: occurrence
|
|
112
122
|
? "Skipped because Slopify was not running at the scheduled time."
|
|
113
|
-
: noTopic
|
|
123
|
+
: (noTopic ?? notReady)
|
|
114
124
|
: null,
|
|
115
125
|
};
|
|
116
126
|
if (!insertRun(deps.db, run))
|