@gentbajko/slopify 3.25.8 → 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/3.25.9.md +5 -0
- package/dist/patch-notes/4.0.0.md +51 -0
- package/dist/patch-notes/index.json +12 -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 +34 -50
- 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,253 @@
|
|
|
1
|
+
import { rmSync } from "node:fs";
|
|
2
|
+
import { strFromU8, unzipSync } from "fflate";
|
|
3
|
+
import { transact } from "../../kernel/db/tx.js";
|
|
4
|
+
import { detectSlots } from "../admission/substitute.js";
|
|
5
|
+
import { nameMax } from "../library/model.js";
|
|
6
|
+
import { entryByName, insertEntry } from "../library/repo.js";
|
|
7
|
+
import { renamedIn } from "../library/used-by.js";
|
|
8
|
+
import { freeTemplateName, placePrompt } from "../onboarding/install.js";
|
|
9
|
+
import { createTemplate } from "../project-templates/service.js";
|
|
10
|
+
import { listVoices } from "../settings/repo.js";
|
|
11
|
+
import { looksSecret, packFormat, packFormatVersion, packLimits, packManifestSchema, packSampleFile, } from "./pack.js";
|
|
12
|
+
import { packSamplesDir, writePackSamples } from "./pack-samples.js";
|
|
13
|
+
const refuse = (message) => ({
|
|
14
|
+
ok: false,
|
|
15
|
+
reason: "invalid-input",
|
|
16
|
+
message,
|
|
17
|
+
});
|
|
18
|
+
const notAPack = "This file isn't a template pack Slopify can read. Choose the .zip that Export pack in a template's studio made.";
|
|
19
|
+
class ZipStopped extends Error {
|
|
20
|
+
}
|
|
21
|
+
// Every check that needs nothing written: a zip, small enough, with a manifest of this format
|
|
22
|
+
// and version, no keys, and every sample it names inside it.
|
|
23
|
+
export function readPack(bytes) {
|
|
24
|
+
if (bytes.byteLength > packLimits.uploadBytes)
|
|
25
|
+
return refuse(`This pack is larger than ${String(packLimits.uploadBytes / 1024 / 1024)} MB, more than a template pack ever is. Export it again from the template's studio, or ask whoever made it to.`);
|
|
26
|
+
let oversized;
|
|
27
|
+
let unexpected;
|
|
28
|
+
let twice;
|
|
29
|
+
let tooMany = false;
|
|
30
|
+
// A zip's central directory can list one compressed block many times over: entries are
|
|
31
|
+
// counted, named once each and capped in total before any of them is unpacked.
|
|
32
|
+
const maxEntries = 1 + packLimits.samples;
|
|
33
|
+
const maxTotal = packLimits.manifestBytes + packLimits.samples * packLimits.sampleBytes;
|
|
34
|
+
const seen = new Set();
|
|
35
|
+
let total = 0;
|
|
36
|
+
let entries;
|
|
37
|
+
try {
|
|
38
|
+
entries = unzipSync(bytes, {
|
|
39
|
+
filter: (file) => {
|
|
40
|
+
if (file.name.endsWith("/"))
|
|
41
|
+
return false;
|
|
42
|
+
if (seen.has(file.name)) {
|
|
43
|
+
twice ??= file.name;
|
|
44
|
+
throw new ZipStopped();
|
|
45
|
+
}
|
|
46
|
+
seen.add(file.name);
|
|
47
|
+
if (seen.size > maxEntries) {
|
|
48
|
+
tooMany = true;
|
|
49
|
+
throw new ZipStopped();
|
|
50
|
+
}
|
|
51
|
+
const known = file.name === "manifest.json" || packSampleFile.test(file.name);
|
|
52
|
+
if (!known) {
|
|
53
|
+
unexpected ??= file.name;
|
|
54
|
+
return false;
|
|
55
|
+
}
|
|
56
|
+
const limit = file.name === "manifest.json" ? packLimits.manifestBytes : packLimits.sampleBytes;
|
|
57
|
+
if (file.originalSize > limit) {
|
|
58
|
+
oversized ??= file.name;
|
|
59
|
+
return false;
|
|
60
|
+
}
|
|
61
|
+
total += file.originalSize;
|
|
62
|
+
if (total > maxTotal) {
|
|
63
|
+
tooMany = true;
|
|
64
|
+
throw new ZipStopped();
|
|
65
|
+
}
|
|
66
|
+
return true;
|
|
67
|
+
},
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
catch (error) {
|
|
71
|
+
if (!(error instanceof ZipStopped))
|
|
72
|
+
return refuse(notAPack);
|
|
73
|
+
entries = {};
|
|
74
|
+
}
|
|
75
|
+
if (tooMany)
|
|
76
|
+
return refuse(`This pack holds more files than a template pack ever has (a manifest and at most ${String(packLimits.samples)} samples), so nothing was imported. Export the pack again from the template's studio.`);
|
|
77
|
+
if (twice !== undefined)
|
|
78
|
+
return refuse(`This pack holds ${twice.slice(0, 120)} twice, which an exported pack never does, so nothing was imported. Export the pack again from the template's studio.`);
|
|
79
|
+
if (oversized !== undefined)
|
|
80
|
+
return refuse(`This pack's ${oversized} is larger than a template pack allows, so nothing was imported. Export the pack again from the template's studio.`);
|
|
81
|
+
if (unexpected !== undefined)
|
|
82
|
+
return refuse(`This pack holds a file a template pack never has (${unexpected.slice(0, 120)}), so nothing was imported. Export the pack again from the template's studio.`);
|
|
83
|
+
const raw = entries["manifest.json"];
|
|
84
|
+
if (raw === undefined)
|
|
85
|
+
return refuse("This zip has no manifest.json, so it isn't a template pack. Choose the .zip that Export pack in a template's studio made.");
|
|
86
|
+
const text = strFromU8(raw);
|
|
87
|
+
if (looksSecret(text))
|
|
88
|
+
return refuse("This pack holds what looks like an API key or a login token, so Slopify didn't import it: keys never travel in a pack. Ask whoever made it to remove it from the template and export it again.");
|
|
89
|
+
let json;
|
|
90
|
+
try {
|
|
91
|
+
json = JSON.parse(text);
|
|
92
|
+
}
|
|
93
|
+
catch {
|
|
94
|
+
return refuse("This pack's manifest.json isn't readable JSON; the file may be damaged. Export the pack again from the template's studio.");
|
|
95
|
+
}
|
|
96
|
+
const head = json;
|
|
97
|
+
if (typeof json !== "object" || json === null || head.format !== packFormat)
|
|
98
|
+
return refuse(notAPack);
|
|
99
|
+
if (typeof head.formatVersion === "number" && head.formatVersion > packFormatVersion)
|
|
100
|
+
return refuse(`This pack was made by a newer Slopify (pack format ${String(head.formatVersion)}). Update Slopify in Settings → About, then import it again.`);
|
|
101
|
+
const parsed = packManifestSchema.safeParse(json);
|
|
102
|
+
if (!parsed.success) {
|
|
103
|
+
const issue = parsed.error.issues[0];
|
|
104
|
+
const where = issue === undefined ? "" : ` (${issue.path.join(".") || "manifest"}: ${issue.message})`;
|
|
105
|
+
return refuse(`This pack's manifest isn't one Slopify can use${where.slice(0, 300)}. It may be damaged or edited by hand; export it again from the template's studio.`);
|
|
106
|
+
}
|
|
107
|
+
const files = new Map();
|
|
108
|
+
for (const sample of parsed.data.samples) {
|
|
109
|
+
const file = entries[sample.file];
|
|
110
|
+
if (file === undefined)
|
|
111
|
+
return refuse(`This pack names a sample (${sample.file}) it doesn't hold; the file may be damaged. Export the pack again from the template's studio.`);
|
|
112
|
+
files.set(sample.file, file);
|
|
113
|
+
}
|
|
114
|
+
return { ok: true, value: { manifest: parsed.data, files } };
|
|
115
|
+
}
|
|
116
|
+
function placeEntry(deps, row, at) {
|
|
117
|
+
for (let n = 1;; n += 1) {
|
|
118
|
+
const suffix = n === 1 ? "" : ` (${String(n)})`;
|
|
119
|
+
const candidate = `${row.name.slice(0, nameMax - suffix.length).trimEnd()}${suffix}`;
|
|
120
|
+
const taken = entryByName(deps.db, row.category, candidate);
|
|
121
|
+
if (taken?.body === row.body)
|
|
122
|
+
return { name: taken.name, added: false };
|
|
123
|
+
if (taken !== undefined)
|
|
124
|
+
continue;
|
|
125
|
+
const entry = {
|
|
126
|
+
id: deps.ids.next(),
|
|
127
|
+
category: row.category,
|
|
128
|
+
mode: row.mode,
|
|
129
|
+
name: candidate,
|
|
130
|
+
body: row.body,
|
|
131
|
+
slots: detectSlots(row.body).names,
|
|
132
|
+
updatedAt: at,
|
|
133
|
+
};
|
|
134
|
+
insertEntry(deps.db, entry);
|
|
135
|
+
return { name: candidate, added: true };
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
// The pack's Library items placed in this Library, and the document naming each by the name it
|
|
139
|
+
// has here.
|
|
140
|
+
function placeLibrary(deps, document, at) {
|
|
141
|
+
const snapshot = document.librarySnapshot ?? { prompts: [], entries: [] };
|
|
142
|
+
let form = document.form;
|
|
143
|
+
let added = 0;
|
|
144
|
+
const renamed = [];
|
|
145
|
+
const prompts = [];
|
|
146
|
+
for (const row of snapshot.prompts) {
|
|
147
|
+
const placed = placePrompt(deps, row.kind, row.name, row.body, at);
|
|
148
|
+
if (placed.added)
|
|
149
|
+
added += 1;
|
|
150
|
+
if (placed.prompt.name !== row.name) {
|
|
151
|
+
renamed.push({ from: row.name, to: placed.prompt.name });
|
|
152
|
+
form =
|
|
153
|
+
renamedIn(form, { item: "prompt", kind: row.kind, name: row.name }, placed.prompt.name) ??
|
|
154
|
+
form;
|
|
155
|
+
}
|
|
156
|
+
prompts.push({ ...row, id: placed.prompt.id, name: placed.prompt.name });
|
|
157
|
+
}
|
|
158
|
+
const entries = [];
|
|
159
|
+
for (const row of snapshot.entries) {
|
|
160
|
+
const placed = placeEntry(deps, row, at);
|
|
161
|
+
if (placed.added)
|
|
162
|
+
added += 1;
|
|
163
|
+
if (placed.name !== row.name) {
|
|
164
|
+
renamed.push({ from: row.name, to: placed.name });
|
|
165
|
+
form =
|
|
166
|
+
renamedIn(form, { item: "entry", category: row.category, name: row.name }, placed.name) ??
|
|
167
|
+
form;
|
|
168
|
+
}
|
|
169
|
+
entries.push({ ...row, name: placed.name });
|
|
170
|
+
}
|
|
171
|
+
return {
|
|
172
|
+
document: {
|
|
173
|
+
...document,
|
|
174
|
+
form: form,
|
|
175
|
+
librarySnapshot: { prompts, entries },
|
|
176
|
+
},
|
|
177
|
+
added,
|
|
178
|
+
renamed,
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
export function importPack(deps, bytes) {
|
|
182
|
+
const read = readPack(bytes);
|
|
183
|
+
if (!read.ok)
|
|
184
|
+
return read;
|
|
185
|
+
const { manifest, files } = read.value;
|
|
186
|
+
const templateId = deps.uuid();
|
|
187
|
+
const at = deps.clock.now().toISOString();
|
|
188
|
+
let made;
|
|
189
|
+
try {
|
|
190
|
+
made = transact(deps.db, () => {
|
|
191
|
+
const placed = placeLibrary(deps, manifest.template.document, at);
|
|
192
|
+
const { channelId: _channel, templateSource: _source, ...document } = placed.document;
|
|
193
|
+
const name = freeTemplateName(deps, manifest.template.name);
|
|
194
|
+
const created = createTemplate(deps, {
|
|
195
|
+
id: templateId,
|
|
196
|
+
name,
|
|
197
|
+
document: { ...document, fontUpload: null },
|
|
198
|
+
});
|
|
199
|
+
if (!created.ok)
|
|
200
|
+
throw new PackRefused(created.reason === "missing-prompt"
|
|
201
|
+
? "This pack's template names a prompt or intro/outro the pack doesn't carry, so it can't be made here. Ask whoever made it to export it again."
|
|
202
|
+
: "Slopify couldn't make a template from this pack. Import it again; if it fails again, use Download diagnostics in Settings and report it.");
|
|
203
|
+
const audio = created.value.document.form.audio;
|
|
204
|
+
const voiceKnown = audio.voice === "" ||
|
|
205
|
+
listVoices(deps.db).some((voice) => voice.provider === audio.provider && voice.voiceId === audio.voice);
|
|
206
|
+
return {
|
|
207
|
+
ok: true,
|
|
208
|
+
value: {
|
|
209
|
+
templateId,
|
|
210
|
+
name,
|
|
211
|
+
added: placed.added,
|
|
212
|
+
renamed: placed.renamed,
|
|
213
|
+
voiceKnown,
|
|
214
|
+
samples: manifest.samples.length,
|
|
215
|
+
},
|
|
216
|
+
};
|
|
217
|
+
});
|
|
218
|
+
}
|
|
219
|
+
catch (error) {
|
|
220
|
+
if (error instanceof PackRefused)
|
|
221
|
+
return refuse(error.message);
|
|
222
|
+
throw error;
|
|
223
|
+
}
|
|
224
|
+
if (!made.ok)
|
|
225
|
+
return made;
|
|
226
|
+
// The samples are only to look at: written once the template is in, a failure to write them
|
|
227
|
+
// leaves the template as it is, without them.
|
|
228
|
+
try {
|
|
229
|
+
writePackSamples(deps.paths, templateId, manifest.samples.flatMap((sample) => {
|
|
230
|
+
const body = files.get(sample.file);
|
|
231
|
+
return body === undefined
|
|
232
|
+
? []
|
|
233
|
+
: [
|
|
234
|
+
{
|
|
235
|
+
file: sample.file.slice("samples/".length),
|
|
236
|
+
role: sample.role,
|
|
237
|
+
name: sample.name,
|
|
238
|
+
body,
|
|
239
|
+
},
|
|
240
|
+
];
|
|
241
|
+
}));
|
|
242
|
+
}
|
|
243
|
+
catch (error) {
|
|
244
|
+
rmSync(packSamplesDir(deps.paths, templateId), { recursive: true, force: true });
|
|
245
|
+
deps.log.write("warn", "template-studio.pack", {
|
|
246
|
+
detail: `The samples of the imported pack ${templateId} weren't kept: ${error instanceof Error ? error.message : String(error)}`,
|
|
247
|
+
});
|
|
248
|
+
return { ok: true, value: { ...made.value, samples: 0 } };
|
|
249
|
+
}
|
|
250
|
+
return made;
|
|
251
|
+
}
|
|
252
|
+
class PackRefused extends Error {
|
|
253
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { existsSync, mkdirSync, readFileSync, renameSync, rmSync, statSync, writeFileSync, } from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { z } from "zod";
|
|
4
|
+
import { packSampleRoles, packSampleType } from "./pack.js";
|
|
5
|
+
// The samples an imported pack came with, kept beside the data dir's studio files so the studio
|
|
6
|
+
// shows them until the template makes its own: `template-studio/packs/<template id>/`, with a
|
|
7
|
+
// samples.json naming each file and what it is.
|
|
8
|
+
export function packSamplesDir(paths, templateId) {
|
|
9
|
+
return join(paths.dataDir, "template-studio", "packs", templateId);
|
|
10
|
+
}
|
|
11
|
+
const fileName = /^[a-z0-9-]{1,40}\.(png|jpg|jpeg|webp|mp3|m4a|wav)$/;
|
|
12
|
+
const listSchema = z.array(z
|
|
13
|
+
.object({
|
|
14
|
+
file: z.string().regex(fileName),
|
|
15
|
+
role: z.enum(packSampleRoles),
|
|
16
|
+
name: z.string().max(200),
|
|
17
|
+
})
|
|
18
|
+
.strict());
|
|
19
|
+
// Written to a folder of its own first and moved into place, so a half-written set is never
|
|
20
|
+
// shown.
|
|
21
|
+
export function writePackSamples(paths, templateId, samples) {
|
|
22
|
+
if (samples.length === 0)
|
|
23
|
+
return;
|
|
24
|
+
const target = packSamplesDir(paths, templateId);
|
|
25
|
+
const staging = `${target}.part`;
|
|
26
|
+
rmSync(staging, { recursive: true, force: true });
|
|
27
|
+
mkdirSync(staging, { recursive: true, mode: 0o700 });
|
|
28
|
+
for (const sample of samples) {
|
|
29
|
+
if (!fileName.test(sample.file))
|
|
30
|
+
continue;
|
|
31
|
+
writeFileSync(join(staging, sample.file), sample.body, { mode: 0o600 });
|
|
32
|
+
}
|
|
33
|
+
writeFileSync(join(staging, "samples.json"), JSON.stringify(samples.map(({ file, role, name }) => ({ file, role, name }))), { mode: 0o600 });
|
|
34
|
+
rmSync(target, { recursive: true, force: true });
|
|
35
|
+
renameSync(staging, target);
|
|
36
|
+
}
|
|
37
|
+
export function packSamples(paths, templateId) {
|
|
38
|
+
const list = join(packSamplesDir(paths, templateId), "samples.json");
|
|
39
|
+
if (!existsSync(list))
|
|
40
|
+
return [];
|
|
41
|
+
try {
|
|
42
|
+
const parsed = listSchema.safeParse(JSON.parse(readFileSync(list, "utf8")));
|
|
43
|
+
return parsed.success ? parsed.data : [];
|
|
44
|
+
}
|
|
45
|
+
catch {
|
|
46
|
+
return [];
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
// One kept sample's file, or undefined when the template has no such sample.
|
|
50
|
+
export function packSampleFileOf(paths, templateId, file) {
|
|
51
|
+
if (!packSamples(paths, templateId).some((sample) => sample.file === file))
|
|
52
|
+
return undefined;
|
|
53
|
+
const path = join(packSamplesDir(paths, templateId), file);
|
|
54
|
+
const type = packSampleType(`samples/${file}`);
|
|
55
|
+
const stat = statSync(path, { throwIfNoEntry: false });
|
|
56
|
+
return stat === undefined || type === undefined ? undefined : { path, bytes: stat.size, type };
|
|
57
|
+
}
|
|
58
|
+
export function removePackSamples(paths, templateId) {
|
|
59
|
+
rmSync(packSamplesDir(paths, templateId), { recursive: true, force: true });
|
|
60
|
+
}
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
import { readFile } from "node:fs/promises";
|
|
2
|
+
import { strToU8, zipSync } from "fflate";
|
|
3
|
+
import { z } from "zod";
|
|
4
|
+
import { redact } from "../../kernel/log.js";
|
|
5
|
+
import { projectExists } from "../admission/repo.js";
|
|
6
|
+
import { playDraftDocumentSchema } from "../play-drafts/schema.js";
|
|
7
|
+
import { templateById } from "../project-templates/repo.js";
|
|
8
|
+
import { templateNameMax } from "../project-templates/schema.js";
|
|
9
|
+
import { templateSetup } from "../project-templates/setup.js";
|
|
10
|
+
import { findRevisionDownload } from "../revisions/downloads.js";
|
|
11
|
+
import { currentRevisionId } from "../revisions/repo.js";
|
|
12
|
+
import { getRevisionView } from "../revisions/view.js";
|
|
13
|
+
import { listVoices } from "../settings/repo.js";
|
|
14
|
+
import { checkedVersion } from "./gate.js";
|
|
15
|
+
import { gatesOf, sampleOf } from "./repo.js";
|
|
16
|
+
// A template pack: a template set up in the studio, as one zip someone else imports and looks
|
|
17
|
+
// at before using it. It carries the version marked ready (its prompts' and intros' text, the
|
|
18
|
+
// voice it picks, the Look, the PDF theme) and a few of its sample's files. Never a key, a
|
|
19
|
+
// login or anything else of this computer's: a pack is made to be shared.
|
|
20
|
+
export const packFormat = "slopify-template-pack";
|
|
21
|
+
export const packFormatVersion = 1;
|
|
22
|
+
// ceiling: a pack is a few sample files and a manifest; a bigger upload is refused before it is
|
|
23
|
+
// read, and each entry is refused by its stated size before it is unpacked.
|
|
24
|
+
export const packLimits = {
|
|
25
|
+
uploadBytes: 30 * 1024 * 1024,
|
|
26
|
+
manifestBytes: 2 * 1024 * 1024,
|
|
27
|
+
sampleBytes: 6 * 1024 * 1024,
|
|
28
|
+
samples: 6,
|
|
29
|
+
pictures: 4,
|
|
30
|
+
};
|
|
31
|
+
export const packSampleRoles = ["thumbnail", "picture", "narration"];
|
|
32
|
+
const sampleTypes = {
|
|
33
|
+
png: "image/png",
|
|
34
|
+
jpg: "image/jpeg",
|
|
35
|
+
jpeg: "image/jpeg",
|
|
36
|
+
webp: "image/webp",
|
|
37
|
+
mp3: "audio/mpeg",
|
|
38
|
+
m4a: "audio/mp4",
|
|
39
|
+
wav: "audio/wav",
|
|
40
|
+
};
|
|
41
|
+
export const packSampleFile = /^samples\/([a-z0-9-]{1,40})\.(png|jpg|jpeg|webp|mp3|m4a|wav)$/;
|
|
42
|
+
export function packSampleType(file) {
|
|
43
|
+
const extension = packSampleFile.exec(file)?.[2];
|
|
44
|
+
return extension === undefined ? undefined : sampleTypes[extension];
|
|
45
|
+
}
|
|
46
|
+
export const packManifestSchema = z
|
|
47
|
+
.object({
|
|
48
|
+
format: z.literal(packFormat),
|
|
49
|
+
formatVersion: z.literal(packFormatVersion),
|
|
50
|
+
exportedAt: z.string().max(40),
|
|
51
|
+
slopifyVersion: z.string().max(40),
|
|
52
|
+
template: z
|
|
53
|
+
.object({
|
|
54
|
+
name: z.string().trim().min(1).max(templateNameMax),
|
|
55
|
+
version: z.number().int().positive(),
|
|
56
|
+
document: playDraftDocumentSchema,
|
|
57
|
+
})
|
|
58
|
+
.strict(),
|
|
59
|
+
// The voice the template picks, as the exporter's Settings → Voices names it.
|
|
60
|
+
voice: z
|
|
61
|
+
.object({
|
|
62
|
+
provider: z.string().min(1).max(100),
|
|
63
|
+
voiceId: z.string().min(1).max(200),
|
|
64
|
+
name: z.string().max(200),
|
|
65
|
+
})
|
|
66
|
+
.strict()
|
|
67
|
+
.nullable(),
|
|
68
|
+
samples: z
|
|
69
|
+
.array(z
|
|
70
|
+
.object({
|
|
71
|
+
file: z.string().regex(packSampleFile),
|
|
72
|
+
role: z.enum(packSampleRoles),
|
|
73
|
+
name: z.string().max(200),
|
|
74
|
+
})
|
|
75
|
+
.strict())
|
|
76
|
+
.max(packLimits.samples),
|
|
77
|
+
})
|
|
78
|
+
.strict();
|
|
79
|
+
// Whether text holds what looks like an API key or a bearer token (`kernel/log.ts`'s filter).
|
|
80
|
+
export function looksSecret(text) {
|
|
81
|
+
return redact(text) !== text;
|
|
82
|
+
}
|
|
83
|
+
const extensionOf = (path) => {
|
|
84
|
+
const extension = path.split(".").pop()?.toLowerCase();
|
|
85
|
+
return extension !== undefined && Object.hasOwn(sampleTypes, extension)
|
|
86
|
+
? extension
|
|
87
|
+
: undefined;
|
|
88
|
+
};
|
|
89
|
+
// The sample's thumbnail, up to four pictures and its narration, as its project holds them now.
|
|
90
|
+
// A file too big for a pack is left out.
|
|
91
|
+
export function sampleFilesOf(deps, templateId) {
|
|
92
|
+
const sample = sampleOf(deps.db, templateId);
|
|
93
|
+
if (sample === undefined || !projectExists(deps.db, sample.projectId))
|
|
94
|
+
return [];
|
|
95
|
+
const revisionId = currentRevisionId(deps.db, sample.projectId);
|
|
96
|
+
if (revisionId === undefined)
|
|
97
|
+
return [];
|
|
98
|
+
const view = getRevisionView({ ...deps, ids: { next: () => "" }, clock: { now: deps.clock.now, sleep: async () => { } } }, sample.projectId, revisionId);
|
|
99
|
+
if (view === undefined)
|
|
100
|
+
return [];
|
|
101
|
+
const wanted = [
|
|
102
|
+
{ role: "thumbnail", roles: ["thumbnail"] },
|
|
103
|
+
{ role: "picture", roles: ["image"] },
|
|
104
|
+
{ role: "narration", roles: ["audio_export", "audio_mp3", "audio_body"] },
|
|
105
|
+
];
|
|
106
|
+
const files = [];
|
|
107
|
+
for (const { role, roles } of wanted) {
|
|
108
|
+
const limit = role === "picture" ? packLimits.pictures : 1;
|
|
109
|
+
let taken = 0;
|
|
110
|
+
for (const wantedRole of roles)
|
|
111
|
+
for (const row of view.outputs) {
|
|
112
|
+
if (taken >= limit)
|
|
113
|
+
break;
|
|
114
|
+
if (!row.selected || !row.available || row.state !== "ready")
|
|
115
|
+
continue;
|
|
116
|
+
if (row.output.role !== wantedRole)
|
|
117
|
+
continue;
|
|
118
|
+
const found = findRevisionDownload(deps, sample.projectId, revisionId, row.recordId);
|
|
119
|
+
if (!found.ok)
|
|
120
|
+
continue;
|
|
121
|
+
const extension = extensionOf(found.download.path);
|
|
122
|
+
if (extension === undefined || found.download.bytes > packLimits.sampleBytes)
|
|
123
|
+
continue;
|
|
124
|
+
taken += 1;
|
|
125
|
+
files.push({
|
|
126
|
+
role,
|
|
127
|
+
name: row.output.path.split("/").pop() ?? `${role}.${extension}`,
|
|
128
|
+
path: found.download.path,
|
|
129
|
+
bytes: found.download.bytes,
|
|
130
|
+
extension,
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
return files;
|
|
135
|
+
}
|
|
136
|
+
const slug = (name) => name
|
|
137
|
+
.toLowerCase()
|
|
138
|
+
.replace(/[^a-z0-9]+/g, "-")
|
|
139
|
+
.replace(/^-+|-+$/g, "")
|
|
140
|
+
.slice(0, 60) || "template";
|
|
141
|
+
// Export pack: the newest version its schedules may run (marked ready, carried, or saved
|
|
142
|
+
// before the studio), with the Library's text of each prompt it names as it is now.
|
|
143
|
+
export async function exportPack(deps, templateId, samples = (id) => sampleFilesOf(deps, id)) {
|
|
144
|
+
const head = templateById(deps.db, templateId);
|
|
145
|
+
if (head === undefined)
|
|
146
|
+
return {
|
|
147
|
+
ok: false,
|
|
148
|
+
reason: "not-found",
|
|
149
|
+
message: "This template no longer exists; it may have been deleted. Go to Library → Templates to see your templates.",
|
|
150
|
+
};
|
|
151
|
+
const runs = checkedVersion(gatesOf(deps.db, templateId), head.version);
|
|
152
|
+
const template = runs === undefined ? undefined : templateById(deps.db, templateId, runs);
|
|
153
|
+
if (template === undefined)
|
|
154
|
+
return {
|
|
155
|
+
ok: false,
|
|
156
|
+
reason: "not-ready",
|
|
157
|
+
message: "No version of this template is marked ready, so there is nothing to put in a pack: a pack carries what you checked. Press Mark ready in the studio, then Export pack.",
|
|
158
|
+
};
|
|
159
|
+
const setup = templateSetup(deps, template.document);
|
|
160
|
+
if (!setup.ok)
|
|
161
|
+
return {
|
|
162
|
+
ok: false,
|
|
163
|
+
reason: "invalid-input",
|
|
164
|
+
message: `Version ${String(template.version)} names a prompt or intro/outro that was deleted, so its text can't go into the pack. Choose another in the studio, Save, Mark ready, then Export pack.`,
|
|
165
|
+
};
|
|
166
|
+
const document = setup.value;
|
|
167
|
+
const { audio } = document.form;
|
|
168
|
+
const saved = listVoices(deps.db).find((voice) => voice.provider === audio.provider && voice.voiceId === audio.voice);
|
|
169
|
+
const files = samples(templateId);
|
|
170
|
+
const named = files.map((file, index) => ({
|
|
171
|
+
...file,
|
|
172
|
+
entry: `samples/${file.role}-${String(index + 1)}.${file.extension}`,
|
|
173
|
+
}));
|
|
174
|
+
const manifest = {
|
|
175
|
+
format: packFormat,
|
|
176
|
+
formatVersion: packFormatVersion,
|
|
177
|
+
exportedAt: deps.clock.now().toISOString(),
|
|
178
|
+
slopifyVersion: deps.version.slice(0, 40),
|
|
179
|
+
template: { name: template.name, version: template.version, document },
|
|
180
|
+
voice: audio.provider === "" || audio.voice === ""
|
|
181
|
+
? null
|
|
182
|
+
: { provider: audio.provider, voiceId: audio.voice, name: saved?.name ?? audio.voice },
|
|
183
|
+
samples: named.map((file) => ({ file: file.entry, role: file.role, name: file.name })),
|
|
184
|
+
};
|
|
185
|
+
const json = JSON.stringify(manifest, null, 2);
|
|
186
|
+
if (looksSecret(json))
|
|
187
|
+
return {
|
|
188
|
+
ok: false,
|
|
189
|
+
reason: "invalid-input",
|
|
190
|
+
message: "Something in this template looks like an API key or a login token (a keyword's value or a prompt's text), so no pack was made: a pack is meant to be shared. Remove it in the studio, Save, Mark ready, then press Export pack again.",
|
|
191
|
+
};
|
|
192
|
+
const entries = { "manifest.json": [strToU8(json), { level: 6 }] };
|
|
193
|
+
for (const file of named) {
|
|
194
|
+
// Pictures and audio are compressed already: stored as they are.
|
|
195
|
+
entries[file.entry] = [new Uint8Array(await readFile(file.path)), { level: 0 }];
|
|
196
|
+
}
|
|
197
|
+
return {
|
|
198
|
+
ok: true,
|
|
199
|
+
value: {
|
|
200
|
+
filename: `${slug(template.name)}.slopify-pack.zip`,
|
|
201
|
+
bytes: zipSync(entries),
|
|
202
|
+
version: template.version,
|
|
203
|
+
},
|
|
204
|
+
};
|
|
205
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { projectExists } from "../admission/repo.js";
|
|
2
|
+
import { templateById } from "../project-templates/repo.js";
|
|
3
|
+
import { currentRevisionId } from "../revisions/repo.js";
|
|
4
|
+
import { getRevisionView } from "../revisions/view.js";
|
|
5
|
+
import { runningJob } from "./jobs.js";
|
|
6
|
+
import { sampleOf, sampleVersions } from "./repo.js";
|
|
7
|
+
import { sectionOfWork } from "./sections.js";
|
|
8
|
+
import { paidStatuses } from "./status.js";
|
|
9
|
+
export function paidView(deps, templateId, head) {
|
|
10
|
+
const versions = sampleVersions(deps.db, templateId);
|
|
11
|
+
const row = versions.find((one) => one.templateVersion === head.version);
|
|
12
|
+
const sample = sampleOf(deps.db, templateId);
|
|
13
|
+
const projectId = sample !== undefined && projectExists(deps.db, sample.projectId) ? sample.projectId : null;
|
|
14
|
+
const revisionId = projectId === null ? null : (currentRevisionId(deps.db, projectId) ?? null);
|
|
15
|
+
const revision = projectId === null || revisionId === null
|
|
16
|
+
? undefined
|
|
17
|
+
: getRevisionView({
|
|
18
|
+
db: deps.db,
|
|
19
|
+
paths: deps.paths,
|
|
20
|
+
log: deps.log,
|
|
21
|
+
ids: { next: () => "" },
|
|
22
|
+
clock: { now: () => new Date(), sleep: async () => undefined },
|
|
23
|
+
}, projectId, revisionId);
|
|
24
|
+
const outputs = revision === undefined || projectId === null || revisionId === null
|
|
25
|
+
? []
|
|
26
|
+
: revision.outputs.flatMap((row) => {
|
|
27
|
+
const section = sectionOfWork(row.workKey);
|
|
28
|
+
if (!row.selected || !row.available || row.state !== "ready" || section === undefined)
|
|
29
|
+
return [];
|
|
30
|
+
return [
|
|
31
|
+
{
|
|
32
|
+
section,
|
|
33
|
+
role: row.output.role,
|
|
34
|
+
url: `/files/${encodeURIComponent(projectId)}/revisions/${encodeURIComponent(revisionId)}/${encodeURIComponent(row.recordId)}`,
|
|
35
|
+
name: row.output.path.split("/").pop() ?? row.output.path,
|
|
36
|
+
},
|
|
37
|
+
];
|
|
38
|
+
});
|
|
39
|
+
const running = runningJob(deps.db);
|
|
40
|
+
return {
|
|
41
|
+
view: {
|
|
42
|
+
projectId,
|
|
43
|
+
revisionId,
|
|
44
|
+
state: row?.state ?? null,
|
|
45
|
+
sentence: row?.heldReason ?? null,
|
|
46
|
+
quotedHigh: row?.quotedHigh ?? null,
|
|
47
|
+
article: revision?.articleMarkdown ?? null,
|
|
48
|
+
outputs,
|
|
49
|
+
versions: versions.map((one) => ({
|
|
50
|
+
version: one.templateVersion,
|
|
51
|
+
state: one.state,
|
|
52
|
+
updatedAt: one.updatedAt,
|
|
53
|
+
})),
|
|
54
|
+
},
|
|
55
|
+
statuses: paidStatuses({
|
|
56
|
+
headVersion: head.version,
|
|
57
|
+
headDocument: head.document,
|
|
58
|
+
versions,
|
|
59
|
+
documentOf: (version) => templateById(deps.db, templateId, version)?.document,
|
|
60
|
+
running: running?.templateId === templateId &&
|
|
61
|
+
running.templateVersion === head.version &&
|
|
62
|
+
running.kind !== "free",
|
|
63
|
+
busyElsewhere: running !== undefined && running.templateId !== templateId,
|
|
64
|
+
}),
|
|
65
|
+
};
|
|
66
|
+
}
|