@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,134 @@
|
|
|
1
|
+
import { existsSync } from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { z } from "zod";
|
|
4
|
+
import { templateSamplesDir } from "../../kernel/paths.js";
|
|
5
|
+
import { carryGate } from "./gate.js";
|
|
6
|
+
import { gateStates } from "./model.js";
|
|
7
|
+
// The clause that keeps the studio's hidden projects (each template's sample and each
|
|
8
|
+
// bake-off copy, `template_hidden_projects`) out of a query over projects. Every list the user
|
|
9
|
+
// sees uses it (`edge/http/template-sample-isolation.test.ts` asks each GET route); the studio
|
|
10
|
+
// and its runner read those projects by id, which it never limits.
|
|
11
|
+
export function notTemplateSample(alias = "projects", column = "id") {
|
|
12
|
+
return `NOT EXISTS (SELECT 1 FROM template_hidden_projects h WHERE h.project_id = ${alias}.${column})`;
|
|
13
|
+
}
|
|
14
|
+
const hiddenProject = "SELECT 1 FROM template_hidden_projects WHERE project_id=?";
|
|
15
|
+
export function isTemplateSample(db, projectId) {
|
|
16
|
+
return db.prepare(hiddenProject).get(projectId) !== undefined;
|
|
17
|
+
}
|
|
18
|
+
// The same question asked on every project event (the shell's tally, the global stream):
|
|
19
|
+
// one prepared statement, reused.
|
|
20
|
+
export function templateSampleCheck(db) {
|
|
21
|
+
const named = db.prepare(hiddenProject);
|
|
22
|
+
return (projectId) => named.get(projectId) !== undefined;
|
|
23
|
+
}
|
|
24
|
+
// Which projects' folders are kept in the data dir (`kernel/paths.ts` samples): a project
|
|
25
|
+
// named by a template's sample row, or one whose folder is already there (a sample whose row
|
|
26
|
+
// went with its template keeps its files where they are until the project is deleted).
|
|
27
|
+
export function sampleProjects(db, dataDir) {
|
|
28
|
+
const named = db.prepare(hiddenProject);
|
|
29
|
+
const root = templateSamplesDir({ dataDir });
|
|
30
|
+
return {
|
|
31
|
+
has: (projectId) => named.get(projectId) !== undefined ||
|
|
32
|
+
(/^[0-9A-Za-z_-]{1,64}$/u.test(projectId) && existsSync(join(root, projectId))),
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
export function templateSampleProjects(db) {
|
|
36
|
+
return new Set(db
|
|
37
|
+
.prepare("SELECT project_id FROM template_hidden_projects")
|
|
38
|
+
.all()
|
|
39
|
+
.map((row) => String(row.project_id)));
|
|
40
|
+
}
|
|
41
|
+
// The template whose sample (or bake-off copy) a project is, with its current name; undefined
|
|
42
|
+
// for any other project.
|
|
43
|
+
export function sampleOwner(db, projectId) {
|
|
44
|
+
const row = db
|
|
45
|
+
.prepare(`SELECT o.template_id AS templateId, r.name AS name FROM (
|
|
46
|
+
SELECT template_id, project_id FROM template_samples
|
|
47
|
+
UNION ALL
|
|
48
|
+
SELECT b.template_id, c.project_id FROM template_bakeoff_clones c
|
|
49
|
+
JOIN template_bakeoffs b ON b.id=c.bakeoff_id
|
|
50
|
+
) o
|
|
51
|
+
JOIN project_templates t ON t.id=o.template_id
|
|
52
|
+
JOIN project_template_revisions r ON r.template_id=t.id AND r.version=t.head_version
|
|
53
|
+
WHERE o.project_id=?`)
|
|
54
|
+
.get(projectId);
|
|
55
|
+
return row === undefined
|
|
56
|
+
? undefined
|
|
57
|
+
: z.object({ templateId: z.string(), name: z.string() }).parse(row);
|
|
58
|
+
}
|
|
59
|
+
const gateRow = z.object({
|
|
60
|
+
version: z.number().int().positive(),
|
|
61
|
+
state: z.enum(gateStates),
|
|
62
|
+
carriedFrom: z.number().int().positive().nullable(),
|
|
63
|
+
});
|
|
64
|
+
export function gatesOf(db, templateId) {
|
|
65
|
+
return db
|
|
66
|
+
.prepare("SELECT version,state,carried_from AS carriedFrom FROM template_gates WHERE template_id=? ORDER BY version")
|
|
67
|
+
.all(templateId)
|
|
68
|
+
.map((row) => gateRow.parse(row));
|
|
69
|
+
}
|
|
70
|
+
export function sampleOf(db, templateId) {
|
|
71
|
+
const row = db
|
|
72
|
+
.prepare("SELECT project_id AS projectId,topic,topic_source AS topicSource,full_script AS fullScript FROM template_samples WHERE template_id=?")
|
|
73
|
+
.get(templateId);
|
|
74
|
+
if (row === undefined)
|
|
75
|
+
return undefined;
|
|
76
|
+
const parsed = z
|
|
77
|
+
.object({
|
|
78
|
+
projectId: z.string(),
|
|
79
|
+
topic: z.string(),
|
|
80
|
+
topicSource: z.enum(["fixed", "queued"]),
|
|
81
|
+
fullScript: z.number(),
|
|
82
|
+
})
|
|
83
|
+
.parse(row);
|
|
84
|
+
return { ...parsed, fullScript: parsed.fullScript === 1 };
|
|
85
|
+
}
|
|
86
|
+
const versionRow = z.object({
|
|
87
|
+
templateVersion: z.number().int().positive(),
|
|
88
|
+
revisionId: z.string().nullable(),
|
|
89
|
+
state: z.enum(["queued", "making", "done", "failed", "skipped", "held"]),
|
|
90
|
+
quotedHigh: z.number().nullable(),
|
|
91
|
+
heldReason: z.string().nullable(),
|
|
92
|
+
updatedAt: z.string(),
|
|
93
|
+
});
|
|
94
|
+
// Each version's paid sample, newest first.
|
|
95
|
+
export function sampleVersions(db, templateId) {
|
|
96
|
+
return db
|
|
97
|
+
.prepare(`SELECT template_version AS templateVersion,revision_id AS revisionId,state,quoted_high AS quotedHigh,
|
|
98
|
+
held_reason AS heldReason,updated_at AS updatedAt
|
|
99
|
+
FROM template_sample_versions WHERE template_id=? ORDER BY template_version DESC`)
|
|
100
|
+
.all(templateId)
|
|
101
|
+
.map((row) => versionRow.parse(row));
|
|
102
|
+
}
|
|
103
|
+
// Records what a version's paid sample is now. A field left out keeps what it was.
|
|
104
|
+
export function setSampleVersion(db, templateId, version, change) {
|
|
105
|
+
db.prepare(`INSERT INTO template_sample_versions (template_id,template_version,revision_id,state,quoted_high,held_reason,updated_at)
|
|
106
|
+
VALUES (?,?,?,?,?,?,?)
|
|
107
|
+
ON CONFLICT(template_id,template_version) DO UPDATE SET state=excluded.state,updated_at=excluded.updated_at,
|
|
108
|
+
revision_id=CASE WHEN ? THEN excluded.revision_id ELSE revision_id END,
|
|
109
|
+
quoted_high=CASE WHEN ? THEN excluded.quoted_high ELSE quoted_high END,
|
|
110
|
+
held_reason=CASE WHEN ? THEN excluded.held_reason ELSE held_reason END`).run(templateId, version, change.revisionId ?? null, change.state, change.quotedHigh ?? null, change.heldReason ?? null, change.at, change.revisionId === undefined ? 0 : 1, change.quotedHigh === undefined ? 0 : 1, change.heldReason === undefined ? 0 : 1);
|
|
111
|
+
}
|
|
112
|
+
// Records the hidden project a template's samples are made on.
|
|
113
|
+
export function upsertSample(db, templateId, sample) {
|
|
114
|
+
db.prepare(`INSERT INTO template_samples (template_id,project_id,topic,topic_source,full_script,created_at)
|
|
115
|
+
VALUES (?,?,?,?,?,?)
|
|
116
|
+
ON CONFLICT(template_id) DO UPDATE SET project_id=excluded.project_id,topic=excluded.topic,
|
|
117
|
+
topic_source=excluded.topic_source,full_script=excluded.full_script`).run(templateId, sample.projectId, sample.topic, sample.topicSource, sample.fullScript ? 1 : 0, sample.at);
|
|
118
|
+
}
|
|
119
|
+
// Records a version's gate: marked ready (with the checks it passed), or carried from the
|
|
120
|
+
// version a system writer made it from. Marking a carried or legacy version ready replaces it.
|
|
121
|
+
export function writeGate(db, templateId, gate) {
|
|
122
|
+
db.prepare(`INSERT INTO template_gates (template_id,version,state,carried_from,checks_json,marked_at)
|
|
123
|
+
VALUES (?,?,?,?,?,?)
|
|
124
|
+
ON CONFLICT(template_id,version) DO UPDATE SET state=excluded.state,
|
|
125
|
+
carried_from=excluded.carried_from,checks_json=excluded.checks_json,marked_at=excluded.marked_at`).run(templateId, gate.version, gate.state, gate.carriedFrom, gate.checksJson ?? null, gate.markedAt ?? null);
|
|
126
|
+
}
|
|
127
|
+
// A version Slopify wrote itself from `parent` (a retired model switched, a restore under a new
|
|
128
|
+
// name) keeps its gate: a runnable parent makes `next` carried, so no schedule stops over a
|
|
129
|
+
// change its user didn't make. Call it in the writer's transaction.
|
|
130
|
+
export function carryGateOnto(db, templateId, parent, next) {
|
|
131
|
+
const carried = carryGate(gatesOf(db, templateId), parent, next);
|
|
132
|
+
if (carried !== undefined)
|
|
133
|
+
writeGate(db, templateId, carried);
|
|
134
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { reviewStages } from "../reviews/model.js";
|
|
2
|
+
// Every stage named, so a new review stage has to be placed here too.
|
|
3
|
+
const reviewNames = {
|
|
4
|
+
article: "Script",
|
|
5
|
+
promise: "The opening's promise",
|
|
6
|
+
narration: "Narration",
|
|
7
|
+
images: "Pictures",
|
|
8
|
+
thumbnail: "Thumbnail",
|
|
9
|
+
shorts: "Shorts",
|
|
10
|
+
};
|
|
11
|
+
const checkpointNames = {
|
|
12
|
+
audio: "Before the narration",
|
|
13
|
+
images: "Before the pictures",
|
|
14
|
+
video: "Before the video",
|
|
15
|
+
};
|
|
16
|
+
// Checkpoints stop before a stage; reviews judge what a stage made. In run order.
|
|
17
|
+
const order = [
|
|
18
|
+
"review:article",
|
|
19
|
+
// Read against the title and thumbnail before the paid narration.
|
|
20
|
+
"review:promise",
|
|
21
|
+
"checkpoint:audio",
|
|
22
|
+
"review:narration",
|
|
23
|
+
"checkpoint:images",
|
|
24
|
+
"review:images",
|
|
25
|
+
"review:thumbnail",
|
|
26
|
+
"checkpoint:video",
|
|
27
|
+
"review:shorts",
|
|
28
|
+
];
|
|
29
|
+
export function reviewsPath(document) {
|
|
30
|
+
const { form } = document;
|
|
31
|
+
const agent = form.reviews?.reviewer === "agent";
|
|
32
|
+
const steps = [];
|
|
33
|
+
for (const stage of form.checkpoints ?? [])
|
|
34
|
+
steps.push({
|
|
35
|
+
id: `checkpoint:${stage}`,
|
|
36
|
+
name: checkpointNames[stage] ?? stage,
|
|
37
|
+
kind: "checkpoint",
|
|
38
|
+
by: "you",
|
|
39
|
+
detail: "The run waits here until you approve it.",
|
|
40
|
+
});
|
|
41
|
+
for (const stage of reviewStages) {
|
|
42
|
+
const mode = form.reviews?.stages[stage]?.mode ?? "off";
|
|
43
|
+
if (mode === "off")
|
|
44
|
+
continue;
|
|
45
|
+
steps.push({
|
|
46
|
+
id: `review:${stage}`,
|
|
47
|
+
name: reviewNames[stage],
|
|
48
|
+
kind: "review",
|
|
49
|
+
by: agent ? "agent" : "model",
|
|
50
|
+
detail: `${agent ? "Your agent answers this" : "The reviewer model checks it"}; ${mode === "redo" ? "a failed item is made again" : "a failed item is flagged for you"}.`,
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
const rank = (step) => {
|
|
54
|
+
const at = order.indexOf(step.id);
|
|
55
|
+
return at === -1 ? order.length : at;
|
|
56
|
+
};
|
|
57
|
+
return steps.toSorted((a, b) => rank(a) - rank(b));
|
|
58
|
+
}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { setImmediate as yieldToLoop } from "node:timers/promises";
|
|
2
|
+
import { errorText } from "../../kernel/errors.js";
|
|
3
|
+
import { claimNextJob, finishJob, recoverJobs } from "./jobs.js";
|
|
4
|
+
// The studio's one sample loop, beside the schedules' (`schedules/scheduler.ts`): it makes one
|
|
5
|
+
// job at a time across every template and never blocks a request, yielding to the event loop
|
|
6
|
+
// between steps.
|
|
7
|
+
export function createSampleRunner(deps) {
|
|
8
|
+
const controller = new AbortController();
|
|
9
|
+
let loop;
|
|
10
|
+
let nudge;
|
|
11
|
+
let pending = false;
|
|
12
|
+
const idleWaiters = [];
|
|
13
|
+
const settleIdle = () => {
|
|
14
|
+
for (const resolve of idleWaiters.splice(0))
|
|
15
|
+
resolve();
|
|
16
|
+
};
|
|
17
|
+
const wait = () => new Promise((resolve) => {
|
|
18
|
+
if (pending || controller.signal.aborted) {
|
|
19
|
+
resolve();
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
nudge = resolve;
|
|
23
|
+
});
|
|
24
|
+
const run = async () => {
|
|
25
|
+
while (!controller.signal.aborted) {
|
|
26
|
+
pending = false;
|
|
27
|
+
await yieldToLoop();
|
|
28
|
+
const job = claimNextJob(deps.db);
|
|
29
|
+
if (job === undefined) {
|
|
30
|
+
settleIdle();
|
|
31
|
+
await wait();
|
|
32
|
+
nudge = undefined;
|
|
33
|
+
continue;
|
|
34
|
+
}
|
|
35
|
+
deps.changed(job.templateId);
|
|
36
|
+
try {
|
|
37
|
+
await deps.make(job, controller.signal);
|
|
38
|
+
}
|
|
39
|
+
catch (error) {
|
|
40
|
+
if (controller.signal.aborted)
|
|
41
|
+
return;
|
|
42
|
+
deps.log.write("error", "template-studio.sample", {
|
|
43
|
+
detail: `The sample job of template ${job.templateId} version ${String(job.templateVersion)} stopped: ${errorText(error)}`,
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
if (controller.signal.aborted)
|
|
47
|
+
return;
|
|
48
|
+
finishJob(deps.db, job.templateId);
|
|
49
|
+
deps.changed(job.templateId);
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
return {
|
|
53
|
+
start: () => {
|
|
54
|
+
if (loop !== undefined)
|
|
55
|
+
return;
|
|
56
|
+
recoverJobs(deps.db);
|
|
57
|
+
loop = run().catch((error) => {
|
|
58
|
+
deps.log.write("error", "template-studio.runner", {
|
|
59
|
+
detail: `The template studio's sample loop stopped: ${errorText(error)}. Restart Slopify to make samples again.`,
|
|
60
|
+
});
|
|
61
|
+
});
|
|
62
|
+
},
|
|
63
|
+
wake: () => {
|
|
64
|
+
pending = true;
|
|
65
|
+
nudge?.();
|
|
66
|
+
},
|
|
67
|
+
stop: async () => {
|
|
68
|
+
controller.abort();
|
|
69
|
+
nudge?.();
|
|
70
|
+
await loop;
|
|
71
|
+
settleIdle();
|
|
72
|
+
},
|
|
73
|
+
idle: () => new Promise((resolve) => {
|
|
74
|
+
if (loop === undefined || controller.signal.aborted) {
|
|
75
|
+
resolve();
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
idleWaiters.push(resolve);
|
|
79
|
+
// Settles at once when the loop is already waiting with nothing to do.
|
|
80
|
+
if (nudge !== undefined && !pending)
|
|
81
|
+
settleIdle();
|
|
82
|
+
}),
|
|
83
|
+
};
|
|
84
|
+
}
|
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { playDraftDocumentSchema } from "../play-drafts/schema.js";
|
|
3
|
+
import { templateById } from "../project-templates/repo.js";
|
|
4
|
+
import { updateTemplate } from "../project-templates/service.js";
|
|
5
|
+
import { readJsonSetting, writeSetting } from "../settings/repo.js";
|
|
6
|
+
import { budgetWords } from "./budget.js";
|
|
7
|
+
import { enqueueJob } from "./jobs.js";
|
|
8
|
+
import { quoteSave } from "./quote.js";
|
|
9
|
+
import { setSampleVersion } from "./repo.js";
|
|
10
|
+
// Save in the studio: the template's next version, and its sample queued at the price the
|
|
11
|
+
// person saw. A price that went up since, a price unknown that wasn't accepted, or a sample
|
|
12
|
+
// past today's budget is refused with the new quote, so nothing is ever spent unseen.
|
|
13
|
+
export const saveStudioSchema = z
|
|
14
|
+
.object({
|
|
15
|
+
mutationId: z.uuid(),
|
|
16
|
+
baseVersion: z.number().int().positive(),
|
|
17
|
+
name: z.string().trim().min(1).max(200),
|
|
18
|
+
document: playDraftDocumentSchema,
|
|
19
|
+
sample: z.enum(["make", "skip"]),
|
|
20
|
+
// The high estimate the Save button showed; ignored when the sample is skipped.
|
|
21
|
+
acknowledgedHigh: z.number().finite().min(0).max(10_000),
|
|
22
|
+
// That some steps have no price, and the person accepts it.
|
|
23
|
+
acknowledgedUnknown: z.boolean().optional(),
|
|
24
|
+
})
|
|
25
|
+
.strict()
|
|
26
|
+
.readonly();
|
|
27
|
+
// A sample may cost a little more than the person saw before it waits: a quarter more and five
|
|
28
|
+
// cents. Above that, or with any price unknown that wasn't accepted, it waits to be asked again.
|
|
29
|
+
export function exceedsAcknowledged(high, acknowledged) {
|
|
30
|
+
return high > acknowledged * 1.25 + 0.05;
|
|
31
|
+
}
|
|
32
|
+
// Kept in the database, so a sample a restart stopped continues on what the person accepted.
|
|
33
|
+
const acceptedKey = "templateStudio.unknownAccepted";
|
|
34
|
+
// ceiling: the newest versions of each template; an older one's sample is never made again.
|
|
35
|
+
const keptVersions = 20;
|
|
36
|
+
const acceptedSchema = z.record(z.string(), z.array(z.number().int().positive()));
|
|
37
|
+
export function storedUnknownAcks(db) {
|
|
38
|
+
const read = () => readJsonSetting(db, acceptedKey, acceptedSchema, {});
|
|
39
|
+
return {
|
|
40
|
+
accept: (id, version) => {
|
|
41
|
+
const all = read();
|
|
42
|
+
const versions = [...new Set([...(all[id] ?? []), version])]
|
|
43
|
+
.toSorted((a, b) => b - a)
|
|
44
|
+
.slice(0, keptVersions);
|
|
45
|
+
writeSetting(db, acceptedKey, JSON.stringify({ ...all, [id]: versions }));
|
|
46
|
+
},
|
|
47
|
+
accepted: (id, version) => (read()[id] ?? []).includes(version),
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
// In memory, for tests and a studio with no database of its own.
|
|
51
|
+
export function createUnknownAcks() {
|
|
52
|
+
// ceiling: one entry per template version saved with unknown prices since boot.
|
|
53
|
+
const accepted = new Set();
|
|
54
|
+
const key = (id, version) => `${id}@${String(version)}`;
|
|
55
|
+
return {
|
|
56
|
+
accept: (id, version) => void accepted.add(key(id, version)),
|
|
57
|
+
accepted: (id, version) => accepted.has(key(id, version)),
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
const money = (value) => `$${value.toFixed(2)}`;
|
|
61
|
+
export async function saveStudio(deps, id, raw) {
|
|
62
|
+
const parsed = saveStudioSchema.safeParse(raw);
|
|
63
|
+
if (!parsed.success)
|
|
64
|
+
return {
|
|
65
|
+
ok: false,
|
|
66
|
+
reason: "invalid-input",
|
|
67
|
+
message: "The studio sent a Save Slopify couldn't read. Reload the page and press Save again.",
|
|
68
|
+
};
|
|
69
|
+
const input = parsed.data;
|
|
70
|
+
const head = templateById(deps.db, id);
|
|
71
|
+
if (head === undefined)
|
|
72
|
+
return {
|
|
73
|
+
ok: false,
|
|
74
|
+
reason: "not-found",
|
|
75
|
+
message: "This template no longer exists; it may have been deleted. Go to Library → Templates to see your templates.",
|
|
76
|
+
};
|
|
77
|
+
const update = {
|
|
78
|
+
id,
|
|
79
|
+
baseVersion: input.baseVersion,
|
|
80
|
+
mutationId: input.mutationId,
|
|
81
|
+
name: input.name,
|
|
82
|
+
document: input.document,
|
|
83
|
+
};
|
|
84
|
+
// The same Save sent again (a retry after a lost answer) answers what it did the first time.
|
|
85
|
+
const last = deps.db.prepare("SELECT mutation_id FROM project_templates WHERE id=?").get(id);
|
|
86
|
+
if (last?.mutation_id === input.mutationId) {
|
|
87
|
+
const replay = updateTemplate(deps.review, update);
|
|
88
|
+
if (!replay.ok)
|
|
89
|
+
return {
|
|
90
|
+
ok: false,
|
|
91
|
+
reason: "conflict",
|
|
92
|
+
message: "This Save was already sent with different settings. Reload the studio to see what was saved.",
|
|
93
|
+
};
|
|
94
|
+
return {
|
|
95
|
+
ok: true,
|
|
96
|
+
value: {
|
|
97
|
+
template: replay.value,
|
|
98
|
+
sample: input.sample === "make" ? "queued" : "skipped",
|
|
99
|
+
quote: null,
|
|
100
|
+
replayed: true,
|
|
101
|
+
},
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
if (head.version !== input.baseVersion)
|
|
105
|
+
return {
|
|
106
|
+
ok: false,
|
|
107
|
+
reason: "conflict",
|
|
108
|
+
message: `This template changed since you opened it: it is now version ${String(head.version)}. Reload the studio to see the change, then make yours again.`,
|
|
109
|
+
};
|
|
110
|
+
let quote = null;
|
|
111
|
+
if (input.sample === "make") {
|
|
112
|
+
const quoted = await quoteSave(deps, id, input.document);
|
|
113
|
+
if (!quoted.ok)
|
|
114
|
+
return quoted;
|
|
115
|
+
quote = quoted.value;
|
|
116
|
+
if (quote.problem !== null)
|
|
117
|
+
return {
|
|
118
|
+
ok: false,
|
|
119
|
+
reason: "invalid-input",
|
|
120
|
+
message: `${quote.problem} Press Save without a sample to keep the change.`,
|
|
121
|
+
quote,
|
|
122
|
+
};
|
|
123
|
+
if (exceedsAcknowledged(quote.high, input.acknowledgedHigh))
|
|
124
|
+
return {
|
|
125
|
+
ok: false,
|
|
126
|
+
reason: "cost-changed",
|
|
127
|
+
message: `The sample now costs about ${money(quote.high)}, more than the ${money(input.acknowledgedHigh)} you saw. Press Save again to accept the new price, or Save without a sample.`,
|
|
128
|
+
quote,
|
|
129
|
+
};
|
|
130
|
+
if (quote.unknown > 0 && input.acknowledgedUnknown !== true)
|
|
131
|
+
return {
|
|
132
|
+
ok: false,
|
|
133
|
+
reason: "cost-changed",
|
|
134
|
+
message: `${String(quote.unknown)} ${quote.unknown === 1 ? "step of this sample has" : "steps of this sample have"} no known price. Tick that you accept unknown costs, then press Save again, or Save without a sample.`,
|
|
135
|
+
quote,
|
|
136
|
+
};
|
|
137
|
+
if (!quote.fits)
|
|
138
|
+
return {
|
|
139
|
+
ok: false,
|
|
140
|
+
reason: "over-budget",
|
|
141
|
+
message: `${budgetWords(quote.budget, quote, "This sample")}, or press Save without a sample.`,
|
|
142
|
+
quote,
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
const saved = updateTemplate(deps.review, update);
|
|
146
|
+
if (!saved.ok)
|
|
147
|
+
return {
|
|
148
|
+
ok: false,
|
|
149
|
+
reason: saved.reason === "not-found"
|
|
150
|
+
? "not-found"
|
|
151
|
+
: saved.reason === "conflict"
|
|
152
|
+
? "conflict"
|
|
153
|
+
: "invalid-input",
|
|
154
|
+
message: saved.reason === "missing-prompt"
|
|
155
|
+
? "A prompt or intro/outro entry this template uses was deleted. Choose another in the template, then press Save again."
|
|
156
|
+
: saved.reason === "conflict"
|
|
157
|
+
? "This template changed while you were editing. Reload the studio to see the latest, then make your change again."
|
|
158
|
+
: "Some template settings are not valid, for example an image you supplied yourself. Check the template's settings and press Save again.",
|
|
159
|
+
};
|
|
160
|
+
const template = saved.value;
|
|
161
|
+
const at = deps.clock.now().toISOString();
|
|
162
|
+
if (quote !== null) {
|
|
163
|
+
// Accepted on this Save: unknown prices the person ticked, or a quote that priced them all.
|
|
164
|
+
if (input.acknowledgedUnknown === true || quote.unknown === 0)
|
|
165
|
+
deps.acks.accept(id, template.version);
|
|
166
|
+
setSampleVersion(deps.db, id, template.version, {
|
|
167
|
+
state: "queued",
|
|
168
|
+
at,
|
|
169
|
+
quotedHigh: quote.high,
|
|
170
|
+
heldReason: null,
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
else
|
|
174
|
+
setSampleVersion(deps.db, id, template.version, { state: "skipped", at });
|
|
175
|
+
enqueueJob(deps.db, {
|
|
176
|
+
templateId: id,
|
|
177
|
+
templateVersion: template.version,
|
|
178
|
+
ackHigh: quote === null ? 0 : Math.max(input.acknowledgedHigh, 0),
|
|
179
|
+
kind: quote === null ? "free" : "save",
|
|
180
|
+
requestedAt: at,
|
|
181
|
+
});
|
|
182
|
+
return {
|
|
183
|
+
ok: true,
|
|
184
|
+
value: { template, sample: quote === null ? "skipped" : "queued", quote, replayed: false },
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
export const makeSampleSchema = z
|
|
188
|
+
.object({
|
|
189
|
+
version: z.number().int().positive(),
|
|
190
|
+
// The price the Make sample button showed.
|
|
191
|
+
acknowledgedHigh: z.number().finite().min(0).max(10_000),
|
|
192
|
+
acknowledgedUnknown: z.boolean().optional(),
|
|
193
|
+
})
|
|
194
|
+
.strict()
|
|
195
|
+
.readonly();
|
|
196
|
+
// Make sample: the current version's paid sample asked for again, at the price shown, after it
|
|
197
|
+
// waited (a higher price, unknown prices, the budget) or failed.
|
|
198
|
+
export function requestSample(deps, id, raw) {
|
|
199
|
+
const parsed = makeSampleSchema.safeParse(raw);
|
|
200
|
+
if (!parsed.success)
|
|
201
|
+
return {
|
|
202
|
+
ok: false,
|
|
203
|
+
reason: "invalid-input",
|
|
204
|
+
message: "The studio sent a request Slopify couldn't read. Reload the page and press Make sample again.",
|
|
205
|
+
};
|
|
206
|
+
const head = templateById(deps.db, id);
|
|
207
|
+
if (head === undefined)
|
|
208
|
+
return {
|
|
209
|
+
ok: false,
|
|
210
|
+
reason: "not-found",
|
|
211
|
+
message: "This template no longer exists; it may have been deleted. Go to Library → Templates to see your templates.",
|
|
212
|
+
};
|
|
213
|
+
if (head.version !== parsed.data.version)
|
|
214
|
+
return {
|
|
215
|
+
ok: false,
|
|
216
|
+
reason: "conflict",
|
|
217
|
+
message: `This template is now version ${String(head.version)}, not the version ${String(parsed.data.version)} you saw. Reload the studio, then press Make sample.`,
|
|
218
|
+
};
|
|
219
|
+
const at = deps.clock.now().toISOString();
|
|
220
|
+
if (parsed.data.acknowledgedUnknown === true)
|
|
221
|
+
deps.acks.accept(id, head.version);
|
|
222
|
+
setSampleVersion(deps.db, id, head.version, {
|
|
223
|
+
state: "queued",
|
|
224
|
+
at,
|
|
225
|
+
quotedHigh: parsed.data.acknowledgedHigh,
|
|
226
|
+
heldReason: null,
|
|
227
|
+
});
|
|
228
|
+
enqueueJob(deps.db, {
|
|
229
|
+
templateId: id,
|
|
230
|
+
templateVersion: head.version,
|
|
231
|
+
ackHigh: parsed.data.acknowledgedHigh,
|
|
232
|
+
kind: "save",
|
|
233
|
+
requestedAt: at,
|
|
234
|
+
});
|
|
235
|
+
return { ok: true, value: { version: head.version } };
|
|
236
|
+
}
|