@gentbajko/slopify 2.4.0 → 3.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/LICENSE +201 -21
- package/NOTICE +12 -0
- package/README.md +21 -40
- package/SUBTITLES.md +1 -1
- package/dist/adapter-registry.js +11 -1
- package/dist/adapters/alignment/cache.js +4 -1
- package/dist/adapters/alignment/ctc.js +25 -23
- package/dist/adapters/alignment/index.js +37 -6
- package/dist/adapters/alignment/multilingual.js +267 -0
- package/dist/adapters/alignment/protocol.js +11 -0
- package/dist/adapters/alignment/quality.js +4 -3
- package/dist/adapters/alignment/sentences.js +95 -0
- package/dist/adapters/alignment/spec.js +24 -0
- package/dist/adapters/alignment/spell.js +769 -0
- package/dist/adapters/alignment/text.js +56 -9
- package/dist/adapters/alignment/window.js +34 -30
- package/dist/adapters/alignment/worker.js +23 -21
- package/dist/adapters/explain.js +11 -11
- package/dist/adapters/host-cli/index.js +55 -5
- package/dist/adapters/host-cli/transport.js +13 -6
- package/dist/adapters/image/bytes.js +2 -2
- package/dist/adapters/image/codex-output.js +60 -18
- package/dist/adapters/image/codex.js +166 -25
- package/dist/adapters/image/fal.js +31 -3
- package/dist/adapters/image/google.js +17 -2
- package/dist/adapters/image/openai.js +45 -16
- package/dist/adapters/image/reference.js +41 -0
- package/dist/adapters/image/replicate.js +14 -2
- package/dist/adapters/image/video.js +2 -2
- package/dist/adapters/key-probes.js +36 -0
- package/dist/adapters/llm/claude-code.js +168 -32
- package/dist/adapters/llm/cli-login-error.js +1 -1
- package/dist/adapters/llm/codex-limits.js +157 -0
- package/dist/adapters/llm/codex-models.js +2 -1
- package/dist/adapters/llm/codex-server-models.js +2 -1
- package/dist/adapters/llm/codex.js +99 -12
- package/dist/adapters/llm/document-workspace.js +2 -2
- package/dist/adapters/llm/gemini-workspace.js +1 -1
- package/dist/adapters/llm/gemini.js +16 -3
- package/dist/adapters/llm/image-workspace.js +58 -0
- package/dist/adapters/llm/openrouter.js +11 -3
- package/dist/adapters/llm/run-cli.js +26 -9
- package/dist/adapters/tts/cartesia.js +10 -0
- package/dist/adapters/tts/elevenlabs.js +31 -9
- package/dist/adapters/tts/inworld-async.js +9 -5
- package/dist/adapters/tts/inworld.js +17 -3
- package/dist/adapters/tts/openai.js +3 -0
- package/dist/adapters/tts/voice-languages.js +80 -0
- package/dist/assets/fonts/noto/NotoSans-Regular.ttf +0 -0
- package/dist/assets/fonts/noto/NotoSansArabic-Regular.ttf +0 -0
- package/dist/assets/fonts/noto/NotoSansDevanagari-Regular.ttf +0 -0
- package/dist/assets/fonts/noto/NotoSansHebrew-Regular.ttf +0 -0
- package/dist/assets/fonts/noto/NotoSansThai-Regular.ttf +0 -0
- package/dist/assets/fonts/noto/OFL.txt +93 -0
- package/dist/assets/fonts/noto/SOURCE.txt +18 -0
- package/dist/assets/models.yaml +7 -0
- package/dist/assets/sample/sample-audiobook.tar +0 -0
- package/dist/assets/sample/sample-podcast.tar +0 -0
- package/dist/assets/sample/sample-project.tar +0 -0
- package/dist/catalog/merge.js +132 -0
- package/dist/catalog/registry.js +33 -12
- package/dist/catalog/runtime-models.js +1 -1
- package/dist/catalog/schema.js +12 -1
- package/dist/catalog/store.js +147 -8
- package/dist/catalog/validate.js +17 -2
- package/dist/compose.yaml +71 -0
- package/dist/edge/autostart/docker-record.js +32 -0
- package/dist/edge/autostart/docker.js +50 -0
- package/dist/edge/autostart/index.js +78 -0
- package/dist/edge/autostart/launcher.js +176 -0
- package/dist/edge/autostart/model.js +3 -0
- package/dist/edge/autostart/native.js +210 -0
- package/dist/edge/autostart/prompt.js +66 -0
- package/dist/edge/autostart/service.js +81 -0
- package/dist/edge/cli.js +57 -51
- package/dist/edge/docker-install/activation.js +56 -0
- package/dist/edge/docker-install/apply.js +458 -0
- package/dist/edge/docker-install/engine.js +362 -0
- package/dist/edge/docker-install/run.js +154 -0
- package/dist/edge/docker-install/state.js +146 -0
- package/dist/edge/{docker-projects → docker-install}/volume.js +1 -5
- package/dist/edge/docker.js +14 -22
- package/dist/edge/events/hub.js +11 -0
- package/dist/edge/host-cli.js +1 -1
- package/dist/edge/http/actions.js +32 -10
- package/dist/edge/http/app.js +60 -2
- package/dist/edge/http/auditions.js +70 -0
- package/dist/edge/http/autostart.js +37 -0
- package/dist/edge/http/backups.js +40 -0
- package/dist/edge/http/channel-memory.js +92 -0
- package/dist/edge/http/channels.js +128 -0
- package/dist/edge/http/entries.js +22 -3
- package/dist/edge/http/files.js +1 -1
- package/dist/edge/http/folder-location.js +1 -1
- package/dist/edge/http/home.js +39 -0
- package/dist/edge/http/host-cli.js +45 -6
- package/dist/edge/http/narration-peaks.js +76 -0
- package/dist/edge/http/onboarding.js +152 -0
- package/dist/edge/http/open-folder.js +1 -1
- package/dist/edge/http/patch-notes.js +45 -0
- package/dist/edge/http/planning.js +4 -2
- package/dist/edge/http/problem.js +1 -0
- package/dist/edge/http/project-create.js +87 -0
- package/dist/edge/http/projects.js +31 -99
- package/dist/edge/http/prompts.js +40 -3
- package/dist/edge/http/pronunciations.js +23 -4
- package/dist/edge/http/providers.js +86 -0
- package/dist/edge/http/reviews.js +121 -0
- package/dist/edge/http/revision-files.js +1 -1
- package/dist/edge/http/revisions.js +1 -1
- package/dist/edge/http/run-cost.js +25 -0
- package/dist/edge/http/schedules.js +111 -6
- package/dist/edge/http/settings.js +128 -6
- package/dist/edge/http/storage-files.js +50 -0
- package/dist/edge/http/storage.js +29 -2
- package/dist/edge/http/studio.js +207 -0
- package/dist/edge/http/style-preview.js +100 -0
- package/dist/edge/http/subtitles.js +1 -1
- package/dist/edge/http/trash.js +69 -0
- package/dist/edge/http/update.js +24 -3
- package/dist/edge/http/whats-new.js +15 -0
- package/dist/edge/http/youtube-edits.js +83 -0
- package/dist/edge/native-update.js +62 -0
- package/dist/host-cli/open-folder.js +10 -10
- package/dist/host-cli/runtime.js +12 -3
- package/dist/kernel/db/migrations/0022-drop-duplicate-held-work.sql +56 -0
- package/dist/kernel/db/migrations/0024-reference-attachments.sql +18 -0
- package/dist/kernel/db/migrations/0025-automatic-reviews.sql +40 -0
- package/dist/kernel/db/migrations/0026-schedule-topic-generation.sql +23 -0
- package/dist/kernel/db/migrations/0027-run-cost.sql +58 -0
- package/dist/kernel/db/migrations/0030-automatic-retries.sql +18 -0
- package/dist/kernel/db/migrations/0031-prompt-history-and-description-edits.sql +33 -0
- package/dist/kernel/db/migrations/0032-channels.sql +65 -0
- package/dist/kernel/db/migrations/0034-script-prompts.sql +23 -0
- package/dist/kernel/db/migrations/0035-narration-aliases.sql +13 -0
- package/dist/kernel/db/migrations/0037-project-uploads.sql +8 -0
- package/dist/kernel/db/migrations/0038-voice-languages.sql +7 -0
- package/dist/kernel/db/migrations/0039-channel-essentials.sql +64 -0
- package/dist/kernel/db/migrations/0040-standalone-usage.sql +35 -0
- package/dist/kernel/paths.js +27 -6
- package/dist/kernel/pipeline.js +4 -1
- package/dist/kernel/ports/host-cli.js +139 -16
- package/dist/kernel/ports/image.js +3 -1
- package/dist/kernel/ports/key-probe.js +1 -0
- package/dist/kernel/ports/languages.js +157 -0
- package/dist/kernel/ports/llm.js +3 -1
- package/dist/kernel/ports/model.js +5 -2
- package/dist/kernel/ports/narration-aliases.js +60 -0
- package/dist/kernel/ports/plan-limits.js +36 -0
- package/dist/kernel/runner/attempt.js +18 -9
- package/dist/kernel/runner/audition.js +36 -0
- package/dist/kernel/runner/graph.js +24 -5
- package/dist/kernel/runner/index.js +35 -5
- package/dist/kernel/runner/meter.js +1 -0
- package/dist/kernel/runner/providers.js +164 -9
- package/dist/kernel/runner/retry-policy.js +43 -0
- package/dist/kernel/runner/standalone.js +134 -0
- package/dist/kernel/runner/work-authority.js +28 -2
- package/dist/main.js +321 -20
- package/dist/patch-notes/0.4.2-to-1.0.md +93 -0
- package/dist/patch-notes/1.0-to-2.0.md +74 -0
- package/dist/patch-notes/2.0-to-3.0.md +257 -0
- package/dist/patch-notes/3.0.0.md +70 -0
- package/dist/patch-notes/index.json +26 -0
- package/dist/slices/admission/model.js +35 -0
- package/dist/slices/admission/repo.js +24 -4
- package/dist/slices/admission/rules.js +126 -1
- package/dist/slices/admission/schema.js +68 -1
- package/dist/slices/admission/short-mode.js +47 -0
- package/dist/slices/admission/start.js +90 -16
- package/dist/slices/article/continuation.js +2 -2
- package/dist/slices/article/provided-entries.js +2 -2
- package/dist/slices/article/run.js +6 -2
- package/dist/slices/article/segments.js +3 -3
- package/dist/slices/backups/files.js +135 -0
- package/dist/slices/backups/folder.js +43 -0
- package/dist/slices/backups/model.js +65 -0
- package/dist/slices/backups/repo.js +28 -0
- package/dist/slices/backups/schedule.js +60 -0
- package/dist/slices/backups/service.js +311 -0
- package/dist/slices/batch/index.js +9 -2
- package/dist/slices/cancel/index.js +1 -1
- package/dist/slices/channels/cast-images.js +110 -0
- package/dist/slices/channels/cast-match.js +32 -0
- package/dist/slices/channels/images.js +19 -0
- package/dist/slices/channels/model.js +5 -0
- package/dist/slices/channels/rebrand.js +100 -0
- package/dist/slices/channels/repo.js +218 -0
- package/dist/slices/channels/runs.js +129 -0
- package/dist/slices/channels/schema.js +129 -0
- package/dist/slices/channels/service.js +180 -0
- package/dist/slices/channels/studio-csv.js +110 -0
- package/dist/slices/channels/videos.js +120 -0
- package/dist/slices/checkpoints/recovery.js +10 -0
- package/dist/slices/control/index.js +2 -1
- package/dist/slices/document/fonts.js +1 -1
- package/dist/slices/document/render.js +1 -1
- package/dist/slices/episodes/related.js +90 -0
- package/dist/slices/episodes/repo.js +60 -0
- package/dist/slices/episodes/service.js +58 -0
- package/dist/slices/episodes/summarize.js +170 -0
- package/dist/slices/estimate/index.js +113 -24
- package/dist/slices/estimate/requests.js +60 -2
- package/dist/slices/fixes/rules.js +73 -0
- package/dist/slices/fonts/coverage.js +82 -0
- package/dist/slices/fonts/sfnt.js +74 -0
- package/dist/slices/images/run.js +8 -5
- package/dist/slices/images/scale.js +84 -0
- package/dist/slices/library/diff.js +122 -0
- package/dist/slices/library/history.js +100 -0
- package/dist/slices/library/model.js +3 -0
- package/dist/slices/library/photorealistic.js +49 -0
- package/dist/slices/library/repo.js +19 -14
- package/dist/slices/library/save.js +62 -12
- package/dist/slices/library/slots.js +15 -3
- package/dist/slices/library/snapshot.js +8 -2
- package/dist/slices/library/used-by.js +153 -0
- package/dist/slices/loudness/level-pieces.js +41 -0
- package/dist/slices/loudness/loudnorm.js +164 -0
- package/dist/slices/loudness/model.js +153 -0
- package/dist/slices/model-upkeep/model.js +10 -0
- package/dist/slices/model-upkeep/switch.js +217 -0
- package/dist/slices/model-upkeep/usage.js +183 -0
- package/dist/slices/narration/aliases-library.js +31 -0
- package/dist/slices/narration/aliases-schema.js +53 -0
- package/dist/slices/narration/aliases.js +49 -0
- package/dist/slices/narration/blocks.js +352 -0
- package/dist/slices/narration/chunk.js +19 -7
- package/dist/slices/narration/concat.js +2 -2
- package/dist/slices/narration/describe.js +45 -0
- package/dist/slices/narration/pauses-model.js +63 -0
- package/dist/slices/narration/pauses.js +269 -0
- package/dist/slices/narration/peaks-model.js +1 -0
- package/dist/slices/narration/peaks.js +94 -0
- package/dist/slices/narration/preparation.js +21 -3
- package/dist/slices/narration/pronunciation-chunks.js +3 -3
- package/dist/slices/narration/pronunciation.js +72 -25
- package/dist/slices/narration/run.js +6 -6
- package/dist/slices/narration/shared-glossary.js +1 -1
- package/dist/slices/narration/steering.js +15 -4
- package/dist/slices/notifications/notifier.js +94 -0
- package/dist/slices/notifications/rules.js +102 -0
- package/dist/slices/notifications/send.js +40 -0
- package/dist/slices/notifications/settings.js +34 -0
- package/dist/slices/onboarding/copy.js +143 -0
- package/dist/slices/onboarding/first-run.js +46 -0
- package/dist/slices/onboarding/install.js +119 -0
- package/dist/slices/onboarding/model.js +64 -0
- package/dist/slices/onboarding/packs.js +491 -0
- package/dist/slices/onboarding/quick-short.js +129 -0
- package/dist/slices/onboarding/sample.js +132 -0
- package/dist/slices/onboarding/state.js +68 -0
- package/dist/slices/onboarding/template.js +73 -0
- package/dist/slices/patch-notes/library.js +73 -0
- package/dist/slices/patch-notes/seen.js +77 -0
- package/dist/slices/play-drafts/convert.js +160 -9
- package/dist/slices/play-drafts/readiness.js +3 -0
- package/dist/slices/play-drafts/repo.js +9 -1
- package/dist/slices/play-drafts/review-inputs.js +22 -3
- package/dist/slices/play-drafts/schema.js +109 -2
- package/dist/slices/play-drafts/service.js +10 -1
- package/dist/slices/play-drafts/start.js +43 -18
- package/dist/slices/play-drafts/uploads.js +2 -1
- package/dist/slices/project-templates/from-project.js +70 -4
- package/dist/slices/project-templates/one-off.js +26 -0
- package/dist/slices/project-templates/repo.js +2 -1
- package/dist/slices/project-templates/service.js +15 -4
- package/dist/slices/project-templates/setup.js +29 -2
- package/dist/slices/rebuild/admission-repo.js +1 -1
- package/dist/slices/rebuild/model.js +6 -0
- package/dist/slices/rebuild/preview-details.js +67 -2
- package/dist/slices/rebuild/preview-plan.js +13 -6
- package/dist/slices/rebuild/recipe-audio-parts.js +40 -7
- package/dist/slices/rebuild/recipe-audio.js +66 -12
- package/dist/slices/rebuild/recipe-build.js +15 -5
- package/dist/slices/rebuild/recipe-cast.js +18 -0
- package/dist/slices/rebuild/recipe-checkpoints.js +11 -9
- package/dist/slices/rebuild/recipe-describe.js +93 -0
- package/dist/slices/rebuild/recipe-edit.js +13 -1
- package/dist/slices/rebuild/recipe-exports.js +62 -8
- package/dist/slices/rebuild/recipe-input-schema.js +43 -1
- package/dist/slices/rebuild/recipe-legacy.js +12 -0
- package/dist/slices/rebuild/recipe-loudness.js +45 -0
- package/dist/slices/rebuild/recipe-model.js +25 -6
- package/dist/slices/rebuild/recipe-pauses.js +7 -0
- package/dist/slices/rebuild/recipe-preparation.js +3 -3
- package/dist/slices/rebuild/recipe-provider-choice.js +11 -1
- package/dist/slices/rebuild/recipe-reference.js +58 -0
- package/dist/slices/rebuild/recipe-reviews.js +117 -0
- package/dist/slices/rebuild/recipe-save.js +4 -1
- package/dist/slices/rebuild/recipe-shorts.js +32 -5
- package/dist/slices/rebuild/recipe-text.js +114 -19
- package/dist/slices/rebuild/recipe-validation.js +29 -3
- package/dist/slices/rebuild/recipe-visual.js +104 -29
- package/dist/slices/rebuild/recipe-voices.js +286 -0
- package/dist/slices/rebuild/recipe-work.js +77 -6
- package/dist/slices/rebuild/recipe-youtube.js +2 -0
- package/dist/slices/rebuild/recovery-conflict.js +7 -3
- package/dist/slices/rebuild/recovery-model.js +3 -0
- package/dist/slices/rebuild/recovery-selection.js +28 -2
- package/dist/slices/rebuild/recovery.js +35 -17
- package/dist/slices/rebuild/repo.js +3 -3
- package/dist/slices/rebuild/retry.js +18 -0
- package/dist/slices/rebuild/review-redo.js +110 -0
- package/dist/slices/rebuild/runtime-actions.js +14 -2
- package/dist/slices/rebuild/runtime-admission.js +4 -1
- package/dist/slices/rebuild/runtime-animate.js +4 -4
- package/dist/slices/rebuild/runtime-article.js +5 -5
- package/dist/slices/rebuild/runtime-document.js +4 -4
- package/dist/slices/rebuild/runtime-export-bed.js +43 -0
- package/dist/slices/rebuild/runtime-export-edit.js +70 -11
- package/dist/slices/rebuild/runtime-export-inputs.js +52 -10
- package/dist/slices/rebuild/runtime-export-short.js +85 -0
- package/dist/slices/rebuild/runtime-export.js +84 -19
- package/dist/slices/rebuild/runtime-figure-card.js +87 -0
- package/dist/slices/rebuild/runtime-image.js +69 -0
- package/dist/slices/rebuild/runtime-local.js +154 -27
- package/dist/slices/rebuild/runtime-materialize.js +8 -0
- package/dist/slices/rebuild/runtime-narration-reuse.js +2 -1
- package/dist/slices/rebuild/runtime-narration-text.js +7 -7
- package/dist/slices/rebuild/runtime-pictures.js +28 -0
- package/dist/slices/rebuild/runtime-piece-label.js +29 -0
- package/dist/slices/rebuild/runtime-provider.js +134 -19
- package/dist/slices/rebuild/runtime-publication.js +21 -5
- package/dist/slices/rebuild/runtime-review.js +237 -0
- package/dist/slices/rebuild/runtime-run.js +17 -11
- package/dist/slices/rebuild/runtime-shorts.js +48 -23
- package/dist/slices/rebuild/runtime-store.js +39 -10
- package/dist/slices/rebuild/runtime-subtitles.js +72 -19
- package/dist/slices/rebuild/runtime-voices.js +103 -0
- package/dist/slices/rebuild/runtime-youtube.js +7 -6
- package/dist/slices/rebuild/service-readiness.js +15 -1
- package/dist/slices/rebuild/soften.js +49 -0
- package/dist/slices/rebuild/transition-repo.js +10 -3
- package/dist/slices/research/run.js +4 -4
- package/dist/slices/research/synthesis.js +2 -2
- package/dist/slices/reviews/model.js +105 -0
- package/dist/slices/reviews/outcome.js +22 -0
- package/dist/slices/reviews/repo.js +106 -0
- package/dist/slices/reviews/rules.js +63 -0
- package/dist/slices/reviews/verdict.js +71 -0
- package/dist/slices/reviews/view.js +30 -0
- package/dist/slices/revisions/adopt-content.js +12 -2
- package/dist/slices/revisions/adopt.js +9 -2
- package/dist/slices/revisions/downloads.js +7 -1
- package/dist/slices/revisions/image-plan.js +183 -0
- package/dist/slices/revisions/mutation-assets.js +43 -9
- package/dist/slices/revisions/mutation-cues.js +71 -0
- package/dist/slices/revisions/mutation-prepare.js +8 -10
- package/dist/slices/revisions/mutation-request.js +2 -1
- package/dist/slices/revisions/mutations.js +97 -3
- package/dist/slices/revisions/schema.js +16 -2
- package/dist/slices/run-cost/limits.js +128 -0
- package/dist/slices/run-cost/meter.js +47 -0
- package/dist/slices/run-cost/panel.js +234 -0
- package/dist/slices/run-cost/pricing.js +92 -0
- package/dist/slices/run-cost/week.js +108 -0
- package/dist/slices/schedules/agenda.js +158 -0
- package/dist/slices/schedules/repo.js +58 -18
- package/dist/slices/schedules/scheduler.js +67 -16
- package/dist/slices/schedules/schema.js +138 -1
- package/dist/slices/schedules/service.js +48 -4
- package/dist/slices/schedules/similar.js +90 -0
- package/dist/slices/schedules/topic-list.js +163 -0
- package/dist/slices/schedules/topics.js +422 -0
- package/dist/slices/settings/first-run.js +102 -0
- package/dist/slices/settings/health.js +190 -0
- package/dist/slices/settings/key-guides.js +128 -0
- package/dist/slices/settings/key-test.js +85 -0
- package/dist/slices/settings/model.js +11 -1
- package/dist/slices/settings/playback.js +15 -0
- package/dist/slices/settings/repo.js +17 -3
- package/dist/slices/settings/voices.js +64 -4
- package/dist/slices/settings/whats-new.js +64 -0
- package/dist/slices/shorts/captions.js +9 -0
- package/dist/slices/shorts/pick.js +3 -3
- package/dist/slices/shorts/prompts.js +1 -1
- package/dist/slices/shorts/render.js +54 -5
- package/dist/slices/storage/asset-name.js +3 -0
- package/dist/slices/storage/backup-export.js +48 -9
- package/dist/slices/storage/backup-format.js +38 -1
- package/dist/slices/storage/backup-import.js +191 -19
- package/dist/slices/storage/delete-project.js +7 -4
- package/dist/slices/storage/documents.js +79 -0
- package/dist/slices/storage/files-location.js +531 -0
- package/dist/slices/storage/layout.js +20 -0
- package/dist/slices/storage/model.js +14 -0
- package/dist/slices/storage/portable.js +95 -12
- package/dist/slices/storage/reconcile.js +19 -2
- package/dist/slices/storage/repo.js +4 -1
- package/dist/slices/storage/schema.js +11 -0
- package/dist/slices/storage/trim.js +146 -0
- package/dist/slices/studio/disclosure.js +74 -0
- package/dist/slices/studio/model.js +28 -0
- package/dist/slices/studio/pack.js +209 -0
- package/dist/slices/studio/settings.js +133 -0
- package/dist/slices/style-preview/images.js +61 -0
- package/dist/slices/style-preview/render.js +161 -0
- package/dist/slices/style-preview/schema.js +45 -0
- package/dist/slices/style-preview/service.js +93 -0
- package/dist/slices/style-preview/settings.js +89 -0
- package/dist/slices/subtitles/captions.js +42 -9
- package/dist/slices/subtitles/model.js +22 -0
- package/dist/slices/subtitles/prepare.js +27 -16
- package/dist/slices/subtitles/transcript.js +1 -1
- package/dist/slices/thumbnail/run.js +7 -7
- package/dist/slices/trash/model.js +25 -0
- package/dist/slices/trash/service.js +273 -0
- package/dist/slices/uploads/repo.js +20 -0
- package/dist/slices/video/ambient-bed-schema.js +15 -0
- package/dist/slices/video/ambient-bed.js +95 -0
- package/dist/slices/video/ambient-mix.js +72 -0
- package/dist/slices/video/audio-inputs.js +2 -2
- package/dist/slices/video/cards.js +5 -2
- package/dist/slices/video/edit-list.js +23 -2
- package/dist/slices/video/edit-settings.js +13 -3
- package/dist/slices/video/ffmpeg.js +63 -9
- package/dist/slices/video/figure-card.js +492 -0
- package/dist/slices/video/figure-spans.js +51 -0
- package/dist/slices/video/plan.js +125 -13
- package/dist/slices/video/run.js +14 -12
- package/dist/slices/video/slideshow.js +39 -4
- package/dist/slices/voices/attribution.js +69 -0
- package/dist/slices/voices/audio-files.js +82 -0
- package/dist/slices/voices/audition.js +25 -0
- package/dist/slices/voices/cast.js +56 -0
- package/dist/slices/voices/grouping.js +49 -0
- package/dist/slices/voices/join.js +35 -0
- package/dist/slices/voices/languages.js +25 -0
- package/dist/slices/voices/model.js +179 -0
- package/dist/slices/voices/palette.js +26 -0
- package/dist/slices/voices/panel.js +91 -0
- package/dist/slices/voices/portraits.js +55 -0
- package/dist/slices/voices/script.js +136 -0
- package/dist/slices/voices/timing.js +58 -0
- package/dist/slices/youtube/answer.js +1 -1
- package/dist/slices/youtube/chapters.js +126 -0
- package/dist/slices/youtube/edits-repo.js +69 -0
- package/dist/slices/youtube/edits.js +89 -0
- package/dist/slices/youtube/placeholders.js +98 -0
- package/dist/slices/youtube/transcript.js +11 -4
- package/dist/updater/service.js +66 -21
- package/dist/web/assets/index-CUA2sUxU.css +1 -0
- package/dist/web/assets/index-CX590kE-.js +303 -0
- package/dist/web/assets/jetbrains-mono-cyrillic-400-normal-BEIGL1Tu.woff2 +0 -0
- package/dist/web/assets/jetbrains-mono-cyrillic-400-normal-ugxPyKxw.woff +0 -0
- package/dist/web/assets/jetbrains-mono-greek-400-normal-B9oWc5Lo.woff +0 -0
- package/dist/web/assets/jetbrains-mono-greek-400-normal-C190GLew.woff2 +0 -0
- package/dist/web/assets/jetbrains-mono-latin-400-normal-6-qcROiO.woff +0 -0
- package/dist/web/assets/jetbrains-mono-latin-400-normal-V6pRDFza.woff2 +0 -0
- package/dist/web/assets/jetbrains-mono-latin-ext-400-normal-Bc8Ftmh3.woff2 +0 -0
- package/dist/web/assets/jetbrains-mono-latin-ext-400-normal-fXTG6kC5.woff +0 -0
- package/dist/web/assets/jetbrains-mono-vietnamese-400-normal-CqNFfHCs.woff +0 -0
- package/dist/web/assets/{pdf-CAJNT254.js → pdf-BG8n_uc5.js} +1 -1
- package/dist/web/index.html +2 -2
- package/package.json +4 -4
- package/dist/edge/docker-launch.js +0 -81
- package/dist/edge/docker-projects/activation.js +0 -35
- package/dist/edge/docker-projects/claims.js +0 -85
- package/dist/edge/docker-projects/committed.js +0 -40
- package/dist/edge/docker-projects/engine.js +0 -461
- package/dist/edge/docker-projects/install.js +0 -254
- package/dist/edge/docker-projects/prune.js +0 -66
- package/dist/edge/docker-projects/recover.js +0 -100
- package/dist/edge/docker-projects/state.js +0 -225
- package/dist/web/assets/index-BGgYMm2u.css +0 -1
- package/dist/web/assets/index-BVVgtntL.js +0 -171
- package/scripts/docker-run.sh +0 -3
- /package/dist/edge/{docker-projects → docker-install}/tree.js +0 -0
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { modelFields } from "../../catalog/validate.js";
|
|
2
|
+
import { stagesOf } from "../../slices/admission/repo.js";
|
|
3
|
+
import { admit } from "../../slices/admission/rules.js";
|
|
4
|
+
import { startRun } from "../../slices/admission/start.js";
|
|
5
|
+
import { castVoicedRun } from "../../slices/channels/runs.js";
|
|
6
|
+
import { resolveFont } from "../../slices/fonts/index.js";
|
|
7
|
+
import { pickTemplates, renderPicked } from "../../slices/library/slots.js";
|
|
8
|
+
import { stagedFiles } from "../../slices/storage/repo.js";
|
|
9
|
+
import { record } from "../../slices/telemetry/record.js";
|
|
10
|
+
// Starts a project from a whole draft, the way POST /api/projects always has: the prompt
|
|
11
|
+
// bodies are read now, the draft is admitted, the project and its first work are committed in
|
|
12
|
+
// one transaction, and only then is the runner woken. "Make a 60-second short" starts its
|
|
13
|
+
// project through the same door.
|
|
14
|
+
export async function createProject(deps, sent) {
|
|
15
|
+
// Cast speakers take the cast's voices as they are now, as a run started from Play does.
|
|
16
|
+
const draft = castVoicedRun(deps.db, sent);
|
|
17
|
+
if (draft.checkpoints?.length)
|
|
18
|
+
return {
|
|
19
|
+
ok: false,
|
|
20
|
+
status: 409,
|
|
21
|
+
detail: "This project has checkpoints turned on, so it must be started from Play with Review and start.",
|
|
22
|
+
fields: [],
|
|
23
|
+
};
|
|
24
|
+
const subtitles = draft.subtitles;
|
|
25
|
+
if (subtitles !== undefined && subtitles.mode !== "off") {
|
|
26
|
+
try {
|
|
27
|
+
await resolveFont(deps.paths, subtitles.fontId);
|
|
28
|
+
}
|
|
29
|
+
catch {
|
|
30
|
+
return {
|
|
31
|
+
ok: false,
|
|
32
|
+
status: 400,
|
|
33
|
+
detail: "The subtitle font you picked is no longer available. Choose another font before starting.",
|
|
34
|
+
fields: [
|
|
35
|
+
{
|
|
36
|
+
field: "subtitles.fontId",
|
|
37
|
+
message: "This font is no longer available. Choose another or upload it again in Settings.",
|
|
38
|
+
},
|
|
39
|
+
],
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
// The bodies are read here, at the click, so an edit made since the prompt was selected is
|
|
44
|
+
// the one that runs.
|
|
45
|
+
const picked = pickTemplates(deps.db, draft);
|
|
46
|
+
const admitted = admit({
|
|
47
|
+
draft: picked.draft,
|
|
48
|
+
staged: stagedFiles(deps.db),
|
|
49
|
+
requiredSlots: picked.requiredSlots,
|
|
50
|
+
});
|
|
51
|
+
const modelErrors = modelFields(picked.draft, deps.catalogue);
|
|
52
|
+
if (!admitted.ok || picked.missing.length > 0 || modelErrors.length > 0)
|
|
53
|
+
return {
|
|
54
|
+
ok: false,
|
|
55
|
+
status: 400,
|
|
56
|
+
detail: "This video cannot start yet. Fix the highlighted fields, then try again.",
|
|
57
|
+
fields: [...picked.missing, ...modelErrors, ...(admitted.ok ? [] : admitted.fields)],
|
|
58
|
+
};
|
|
59
|
+
const storage = {
|
|
60
|
+
catalogue: deps.catalogue,
|
|
61
|
+
db: deps.db,
|
|
62
|
+
paths: deps.paths,
|
|
63
|
+
ids: deps.ids,
|
|
64
|
+
clock: deps.clock,
|
|
65
|
+
log: deps.log,
|
|
66
|
+
emit: (event) => {
|
|
67
|
+
deps.hub.emitGlobal(event);
|
|
68
|
+
},
|
|
69
|
+
};
|
|
70
|
+
// Rendered from the values admit() has trimmed, so the stored text carries no padding the
|
|
71
|
+
// user did not intend.
|
|
72
|
+
const { project } = startRun(storage, admitted.draft, renderPicked(picked, admitted.draft.values), false, Object.fromEntries(picked.bodies.map(({ key, body }) => [key, body])));
|
|
73
|
+
const telemetry = {
|
|
74
|
+
db: deps.db,
|
|
75
|
+
ids: deps.ids,
|
|
76
|
+
clock: deps.clock,
|
|
77
|
+
log: deps.log,
|
|
78
|
+
appVersion: deps.version,
|
|
79
|
+
};
|
|
80
|
+
// One event per project created. record() swallows its own failures, so a broken telemetry
|
|
81
|
+
// write cannot cost the user the run.
|
|
82
|
+
record(telemetry, "project.created", {});
|
|
83
|
+
deps.flushSoon();
|
|
84
|
+
// The run starts only once the project is committed.
|
|
85
|
+
deps.runner.tick(project.id);
|
|
86
|
+
return { ok: true, project, stages: stagesOf(deps.db, project.id) };
|
|
87
|
+
}
|
|
@@ -1,21 +1,19 @@
|
|
|
1
1
|
import { zValidator } from "@hono/zod-validator";
|
|
2
2
|
import { Hono } from "hono";
|
|
3
3
|
import { z } from "zod";
|
|
4
|
-
import { modelFields } from "../../catalog/validate.js";
|
|
5
4
|
import { derive, progressOf } from "../../kernel/runner/graph.js";
|
|
6
|
-
import { listProjects, projectById, runDraftSchema, stageStandingsByProject, stagesOf, } from "../../slices/admission/repo.js";
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
5
|
+
import { listProjects, projectById, projectTrashed, runDraftSchema, stageStandingsByProject, stagesOf, } from "../../slices/admission/repo.js";
|
|
6
|
+
import { defaultChannelId } from "../../slices/channels/model.js";
|
|
7
|
+
import { projectChannels } from "../../slices/channels/repo.js";
|
|
9
8
|
import { withProjectControl } from "../../slices/control/lock.js";
|
|
10
|
-
import { resolveFont } from "../../slices/fonts/index.js";
|
|
11
|
-
import { pickTemplates, renderPicked } from "../../slices/library/slots.js";
|
|
12
9
|
import { resumable } from "../../slices/rebuild/recovery-repo.js";
|
|
13
10
|
import { adoptBaseline } from "../../slices/revisions/adopt.js";
|
|
14
11
|
import { currentRevisionId } from "../../slices/revisions/repo.js";
|
|
15
|
-
import {
|
|
16
|
-
import {
|
|
17
|
-
import {
|
|
12
|
+
import { outputsOf } from "../../slices/storage/repo.js";
|
|
13
|
+
import { trashProject } from "../../slices/trash/service.js";
|
|
14
|
+
import { uploadedProjects } from "../../slices/uploads/repo.js";
|
|
18
15
|
import { onInvalid, problem, titleOf } from "./problem.js";
|
|
16
|
+
import { createProject } from "./project-create.js";
|
|
19
17
|
const idParam = z.object({
|
|
20
18
|
id: z
|
|
21
19
|
.string()
|
|
@@ -26,28 +24,11 @@ const idParam = z.object({
|
|
|
26
24
|
// The return type is inferred so Hono keeps the route types the SPA's client is
|
|
27
25
|
// generated from; see stagingRoutes.
|
|
28
26
|
export function projectRoutes(deps) {
|
|
29
|
-
const
|
|
30
|
-
catalogue: deps.catalogue,
|
|
27
|
+
const storageForTrash = {
|
|
31
28
|
db: deps.db,
|
|
32
29
|
paths: deps.paths,
|
|
33
|
-
ids: deps.ids,
|
|
34
30
|
clock: deps.clock,
|
|
35
31
|
log: deps.log,
|
|
36
|
-
emit: (event) => {
|
|
37
|
-
deps.hub.emitGlobal(event);
|
|
38
|
-
},
|
|
39
|
-
};
|
|
40
|
-
const telemetry = {
|
|
41
|
-
db: deps.db,
|
|
42
|
-
ids: deps.ids,
|
|
43
|
-
clock: deps.clock,
|
|
44
|
-
log: deps.log,
|
|
45
|
-
appVersion: deps.version,
|
|
46
|
-
};
|
|
47
|
-
const storageForDelete = {
|
|
48
|
-
db: deps.db,
|
|
49
|
-
paths: deps.paths,
|
|
50
|
-
log: deps.log,
|
|
51
32
|
hasInflight: deps.runner.hasInflight,
|
|
52
33
|
};
|
|
53
34
|
const summarise = (project) => ({
|
|
@@ -56,76 +37,33 @@ export function projectRoutes(deps) {
|
|
|
56
37
|
});
|
|
57
38
|
return (new Hono()
|
|
58
39
|
.post("/", zValidator("json", runDraftSchema, onInvalid), async (c) => {
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
status: 409,
|
|
62
|
-
title: titleOf(409),
|
|
63
|
-
detail: "This project has checkpoints turned on, so it must be started from Play with Review and start.",
|
|
64
|
-
});
|
|
65
|
-
const requestedSubtitles = c.req.valid("json").subtitles;
|
|
66
|
-
if (requestedSubtitles !== undefined && requestedSubtitles.mode !== "off") {
|
|
67
|
-
try {
|
|
68
|
-
await resolveFont(deps.paths, requestedSubtitles.fontId);
|
|
69
|
-
}
|
|
70
|
-
catch {
|
|
71
|
-
return problem(c, {
|
|
72
|
-
status: 400,
|
|
73
|
-
title: titleOf(400),
|
|
74
|
-
detail: "The subtitle font you picked is no longer available. Choose another font before starting.",
|
|
75
|
-
extensions: {
|
|
76
|
-
fields: [
|
|
77
|
-
{
|
|
78
|
-
field: "subtitles.fontId",
|
|
79
|
-
message: "This font is no longer available. Choose another or upload it again in Settings.",
|
|
80
|
-
},
|
|
81
|
-
],
|
|
82
|
-
},
|
|
83
|
-
});
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
// The bodies are read here, at the click, so an edit made since the
|
|
87
|
-
// prompt was selected is the one that runs.
|
|
88
|
-
const picked = pickTemplates(deps.db, c.req.valid("json"));
|
|
89
|
-
const admitted = admit({
|
|
90
|
-
draft: picked.draft,
|
|
91
|
-
staged: stagedFiles(deps.db),
|
|
92
|
-
requiredSlots: picked.requiredSlots,
|
|
93
|
-
});
|
|
94
|
-
const modelErrors = modelFields(picked.draft, deps.catalogue);
|
|
95
|
-
if (!admitted.ok || picked.missing.length > 0 || modelErrors.length > 0) {
|
|
40
|
+
const created = await createProject(deps, c.req.valid("json"));
|
|
41
|
+
if (!created.ok)
|
|
96
42
|
return problem(c, {
|
|
97
|
-
status:
|
|
98
|
-
title: titleOf(
|
|
99
|
-
detail:
|
|
100
|
-
extensions: {
|
|
101
|
-
fields: [...picked.missing, ...modelErrors, ...(admitted.ok ? [] : admitted.fields)],
|
|
102
|
-
},
|
|
43
|
+
status: created.status,
|
|
44
|
+
title: titleOf(created.status),
|
|
45
|
+
detail: created.detail,
|
|
46
|
+
...(created.fields.length === 0 ? {} : { extensions: { fields: created.fields } }),
|
|
103
47
|
});
|
|
104
|
-
}
|
|
105
|
-
// Rendered from the values admit() has trimmed, so the stored text carries no
|
|
106
|
-
// padding the user did not intend.
|
|
107
|
-
const { project } = startRun(storage, admitted.draft, renderPicked(picked, admitted.draft.values), false, Object.fromEntries(picked.bodies.map(({ key, body }) => [key, body])));
|
|
108
|
-
// One event per project created. record() swallows its own
|
|
109
|
-
// failures, so a broken telemetry write cannot cost the user the run.
|
|
110
|
-
record(telemetry, "project.created", {});
|
|
111
|
-
deps.flushSoon();
|
|
112
|
-
// The run starts only once the project is committed.
|
|
113
|
-
deps.runner.tick(project.id);
|
|
114
48
|
// Read back after the tick, not from the rows startRun built: the runner has
|
|
115
49
|
// already claimed every eligible stage, and a body that paired status "running"
|
|
116
50
|
// with "video: pending" would contradict itself.
|
|
117
|
-
return c.json({ project: summarise(project), stages: stagesOf(deps.db, project.id) }, 201);
|
|
51
|
+
return c.json({ project: summarise(created.project), stages: stagesOf(deps.db, created.project.id) }, 201);
|
|
118
52
|
})
|
|
119
53
|
// One statement for every project's stage standings, not one per row: the list needs
|
|
120
54
|
// a status word and a meter, and both come out of the same five columns.
|
|
121
55
|
.get("/", (c) => {
|
|
122
56
|
const standings = stageStandingsByProject(deps.db);
|
|
57
|
+
const channels = projectChannels(deps.db);
|
|
58
|
+
const uploads = uploadedProjects(deps.db);
|
|
123
59
|
const projects = listProjects(deps.db).map((project) => {
|
|
124
60
|
const stages = standings.get(project.id) ?? [];
|
|
125
61
|
return {
|
|
126
62
|
...project,
|
|
127
63
|
status: derive(stages, project.paused),
|
|
128
64
|
progress: progressOf(stages),
|
|
65
|
+
channelId: channels.get(project.id) ?? defaultChannelId,
|
|
66
|
+
uploadedAt: uploads.get(project.id) ?? null,
|
|
129
67
|
};
|
|
130
68
|
});
|
|
131
69
|
return c.json({ projects });
|
|
@@ -136,7 +74,9 @@ export function projectRoutes(deps) {
|
|
|
136
74
|
return problem(c, {
|
|
137
75
|
status: 404,
|
|
138
76
|
title: titleOf(404),
|
|
139
|
-
detail:
|
|
77
|
+
detail: projectTrashed(deps.db, c.req.valid("param").id)
|
|
78
|
+
? "This project is in the trash. Restore it in Settings → Trash to open it again."
|
|
79
|
+
: "This project no longer exists. Go back to Projects to pick another.",
|
|
140
80
|
});
|
|
141
81
|
}
|
|
142
82
|
if (deps.catalogue !== undefined)
|
|
@@ -149,30 +89,22 @@ export function projectRoutes(deps) {
|
|
|
149
89
|
outputs: outputsOf(deps.db, project.id),
|
|
150
90
|
});
|
|
151
91
|
})
|
|
152
|
-
//
|
|
153
|
-
//
|
|
92
|
+
// Moves the project to the trash (Settings → Trash) for 30 days; removing it for good is
|
|
93
|
+
// Delete now there, or the daily purge (`slices/trash`).
|
|
154
94
|
.delete("/:id", zValidator("param", idParam, onInvalid), (c) => {
|
|
155
95
|
const id = c.req.valid("param").id;
|
|
156
96
|
return withProjectControl(deps.db, id, () => {
|
|
157
|
-
const result =
|
|
97
|
+
const result = trashProject(storageForTrash, id);
|
|
158
98
|
if (result.ok)
|
|
159
99
|
return c.body(null, 204);
|
|
100
|
+
const status = result.reason === "running" ? 409 : 404;
|
|
160
101
|
return problem(c, {
|
|
161
|
-
status
|
|
162
|
-
title: titleOf(
|
|
163
|
-
detail: result.
|
|
102
|
+
status,
|
|
103
|
+
title: titleOf(status),
|
|
104
|
+
detail: result.reason === "running"
|
|
105
|
+
? "This project is still running. Use Cancel run on the project page first, then delete it."
|
|
106
|
+
: "This project no longer exists. Go back to Projects to pick another.",
|
|
164
107
|
});
|
|
165
108
|
});
|
|
166
109
|
}));
|
|
167
110
|
}
|
|
168
|
-
const deleteStatus = {
|
|
169
|
-
"no-project": 404,
|
|
170
|
-
running: 409,
|
|
171
|
-
files: 500,
|
|
172
|
-
};
|
|
173
|
-
const deleteDetails = {
|
|
174
|
-
"no-project": "This project no longer exists. Go back to Projects to pick another.",
|
|
175
|
-
// The run has to be stopped before its files can go.
|
|
176
|
-
running: "This project is still running. Use Cancel run on the project page first, then delete it.",
|
|
177
|
-
files: "Some of this project's files could not be removed. Close any program using files in the project folder, then try deleting again.",
|
|
178
|
-
};
|
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
import { zValidator } from "@hono/zod-validator";
|
|
2
2
|
import { Hono } from "hono";
|
|
3
3
|
import { z } from "zod";
|
|
4
|
+
import { versionsOrCurrent } from "../../slices/library/history.js";
|
|
4
5
|
import { promptKinds } from "../../slices/library/model.js";
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
6
|
+
import { setPromptPhotorealistic, withPhotorealistic, } from "../../slices/library/photorealistic.js";
|
|
7
|
+
import { listPrompts, promptById } from "../../slices/library/repo.js";
|
|
8
|
+
import { createPrompt, removePrompt, restorePrompt, updatePrompt, } from "../../slices/library/save.js";
|
|
9
|
+
import { usedBy } from "../../slices/library/used-by.js";
|
|
7
10
|
import { onInvalid, problem, titleOf } from "./problem.js";
|
|
8
11
|
const idParam = z.object({
|
|
9
12
|
id: z
|
|
@@ -12,6 +15,8 @@ const idParam = z.object({
|
|
|
12
15
|
.max(64)
|
|
13
16
|
.regex(/^[0-9A-Za-z_-]+$/),
|
|
14
17
|
});
|
|
18
|
+
// History's Restore names the version by its number.
|
|
19
|
+
export const versionParam = idParam.extend({ version: z.coerce.number().int().positive() });
|
|
15
20
|
// Shape only. The rules - trimming, emptiness, length, the slot lint - are the slice's,
|
|
16
21
|
// so one set of messages reaches the editor.
|
|
17
22
|
const promptBody = z.object({
|
|
@@ -26,7 +31,7 @@ export function promptRoutes(deps) {
|
|
|
26
31
|
return (new Hono()
|
|
27
32
|
// Every kind in one list: 04 Prompts filters by tab, and Duplicate needs the body it
|
|
28
33
|
// is copying.
|
|
29
|
-
.get("/", (c) => c.json({ prompts: listPrompts(deps.db) }))
|
|
34
|
+
.get("/", (c) => c.json({ prompts: withPhotorealistic(deps.db, listPrompts(deps.db)) }))
|
|
30
35
|
.post("/", zValidator("json", promptBody, onInvalid), (c) => {
|
|
31
36
|
const result = createPrompt(library, c.req.valid("json"));
|
|
32
37
|
return result.ok ? c.json(result.value, 201) : refused(c, result, "prompt");
|
|
@@ -34,6 +39,38 @@ export function promptRoutes(deps) {
|
|
|
34
39
|
.put("/:id", zValidator("param", idParam, onInvalid), zValidator("json", promptBody, onInvalid), (c) => {
|
|
35
40
|
const result = updatePrompt(library, c.req.valid("param").id, c.req.valid("json"));
|
|
36
41
|
return result.ok ? c.json(result.value) : refused(c, result, "prompt");
|
|
42
|
+
})
|
|
43
|
+
// An Image prompt's "Draws photorealistic pictures" tick (`slices/library/photorealistic.ts`).
|
|
44
|
+
.put("/:id/photorealistic", zValidator("param", idParam, onInvalid), zValidator("json", z.object({ photorealistic: z.boolean() }), onInvalid), (c) => {
|
|
45
|
+
const result = setPromptPhotorealistic(deps.db, c.req.valid("param").id, c.req.valid("json").photorealistic);
|
|
46
|
+
if (result.ok)
|
|
47
|
+
return c.body(null, 204);
|
|
48
|
+
return result.reason === "not-found"
|
|
49
|
+
? refused(c, { ok: false, reason: "not-found" }, "prompt")
|
|
50
|
+
: problem(c, {
|
|
51
|
+
status: 400,
|
|
52
|
+
title: titleOf(400),
|
|
53
|
+
detail: "Only an Image prompt can be marked photorealistic. Open the prompt from the Image tab in Library → Prompts.",
|
|
54
|
+
});
|
|
55
|
+
})
|
|
56
|
+
// Every saved version, newest first (`slices/library/history.ts`).
|
|
57
|
+
.get("/:id/history", zValidator("param", idParam, onInvalid), (c) => {
|
|
58
|
+
const prompt = promptById(deps.db, c.req.valid("param").id);
|
|
59
|
+
return prompt === undefined
|
|
60
|
+
? refused(c, { ok: false, reason: "not-found" }, "prompt")
|
|
61
|
+
: c.json({ versions: versionsOrCurrent(deps.db, "prompt", prompt) });
|
|
62
|
+
})
|
|
63
|
+
.post("/:id/history/:version/restore", zValidator("param", versionParam, onInvalid), (c) => {
|
|
64
|
+
const { id, version } = c.req.valid("param");
|
|
65
|
+
const result = restorePrompt(library, id, version);
|
|
66
|
+
return result.ok ? c.json(result.value) : refused(c, result, "prompt");
|
|
67
|
+
})
|
|
68
|
+
// The templates, schedules and projects that name it.
|
|
69
|
+
.get("/:id/used-by", zValidator("param", idParam, onInvalid), (c) => {
|
|
70
|
+
const prompt = promptById(deps.db, c.req.valid("param").id);
|
|
71
|
+
return prompt === undefined
|
|
72
|
+
? refused(c, { ok: false, reason: "not-found" }, "prompt")
|
|
73
|
+
: c.json(usedBy(deps.db, { item: "prompt", kind: prompt.kind, name: prompt.name }));
|
|
37
74
|
})
|
|
38
75
|
// A project holds its own rendered text, so nothing cascades and a
|
|
39
76
|
// template used by past projects is deleted like any other.
|
|
@@ -1,17 +1,36 @@
|
|
|
1
1
|
import { zValidator } from "@hono/zod-validator";
|
|
2
2
|
import { Hono } from "hono";
|
|
3
3
|
import { z } from "zod";
|
|
4
|
+
import { aliasCountMax } from "../../kernel/ports/narration-aliases.js";
|
|
5
|
+
import { listNarrationAliases, saveNarrationAliases, } from "../../slices/narration/aliases-library.js";
|
|
4
6
|
import { collectSharedGlossary } from "../../slices/narration/shared-glossary.js";
|
|
5
|
-
import { onInvalid } from "./problem.js";
|
|
7
|
+
import { onInvalid, problem, titleOf } from "./problem.js";
|
|
6
8
|
// The pronunciations of every project's glossary, merged, for Edit project to copy into a
|
|
7
9
|
// project that shares them. `except` leaves out the project being edited, whose own glossary
|
|
8
|
-
// already comes first.
|
|
10
|
+
// already comes first. Library → Aliases lives here too: the other way to change how a word
|
|
11
|
+
// is said.
|
|
9
12
|
export function pronunciationRoutes(deps) {
|
|
10
|
-
|
|
13
|
+
const library = { db: deps.db, ids: deps.ids, clock: deps.clock };
|
|
14
|
+
return (new Hono()
|
|
15
|
+
.get("/shared", zValidator("query", z.object({
|
|
11
16
|
except: z
|
|
12
17
|
.string()
|
|
13
18
|
.max(64)
|
|
14
19
|
.regex(/^[0-9A-Za-z_-]*$/)
|
|
15
20
|
.optional(),
|
|
16
|
-
}), onInvalid), (c) => c.json(collectSharedGlossary(deps, c.req.valid("query").except)))
|
|
21
|
+
}), onInvalid), (c) => c.json(collectSharedGlossary(deps, c.req.valid("query").except)))
|
|
22
|
+
.get("/aliases", (c) => c.json({ aliases: listNarrationAliases(deps.db) }))
|
|
23
|
+
// The editor saves the whole ordered list at once; each row is checked in the slice, so
|
|
24
|
+
// the editor gets "Alias 3: …" sentences rather than schema paths.
|
|
25
|
+
.put("/aliases", zValidator("json", z.object({ aliases: z.array(z.unknown()).max(aliasCountMax * 2) }), onInvalid), (c) => {
|
|
26
|
+
const result = saveNarrationAliases(library, c.req.valid("json").aliases);
|
|
27
|
+
if (result.ok)
|
|
28
|
+
return c.json({ aliases: result.aliases });
|
|
29
|
+
return problem(c, {
|
|
30
|
+
status: 400,
|
|
31
|
+
title: titleOf(400),
|
|
32
|
+
detail: `These aliases can't be saved yet: ${result.fields.map((field) => field.message).join(" ")}`,
|
|
33
|
+
extensions: { fields: result.fields },
|
|
34
|
+
});
|
|
35
|
+
}));
|
|
17
36
|
}
|
|
@@ -1,8 +1,16 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
1
2
|
import { zValidator } from "@hono/zod-validator";
|
|
2
3
|
import { Hono } from "hono";
|
|
3
4
|
import { z } from "zod";
|
|
5
|
+
import { keyProbes } from "../../adapters/key-probes.js";
|
|
4
6
|
import { videoModelsOf } from "../../catalog/schema.js";
|
|
7
|
+
import { switchAllRetired, switchRetiredModel } from "../../slices/model-upkeep/switch.js";
|
|
8
|
+
import { retiredModelUsage, usageKinds, usageSlots } from "../../slices/model-upkeep/usage.js";
|
|
5
9
|
import { cliPathMaxLength, saveCliPath } from "../../slices/settings/cli-paths.js";
|
|
10
|
+
import { dismissFirstRun, firstRunStatus } from "../../slices/settings/first-run.js";
|
|
11
|
+
import { checkProviderHealth } from "../../slices/settings/health.js";
|
|
12
|
+
import { keyGuides } from "../../slices/settings/key-guides.js";
|
|
13
|
+
import { testProviderKey } from "../../slices/settings/key-test.js";
|
|
6
14
|
import { keyStatus, removeProviderKey, saveProviderKey } from "../../slices/settings/keys.js";
|
|
7
15
|
import { isLocalCliProvider, providerById, providerIds } from "../../slices/settings/model.js";
|
|
8
16
|
import { createModelCatalog } from "../../slices/settings/models.js";
|
|
@@ -14,6 +22,17 @@ const providerParam = z.object({ id: z.enum(providerIds) });
|
|
|
14
22
|
// ever issues something longer.
|
|
15
23
|
const pathBody = z.object({ path: z.string().max(cliPathMaxLength) });
|
|
16
24
|
const keyBody = z.object({ key: z.string().min(1).max(4096) });
|
|
25
|
+
const modelId = z.string().min(1).max(200);
|
|
26
|
+
const switchBody = z
|
|
27
|
+
.object({
|
|
28
|
+
kind: z.enum(usageKinds),
|
|
29
|
+
id: z.string().min(1).max(64),
|
|
30
|
+
slot: z.enum(usageSlots),
|
|
31
|
+
from: z.object({ provider: z.string().min(1).max(64), model: modelId }).strict(),
|
|
32
|
+
to: modelId,
|
|
33
|
+
})
|
|
34
|
+
.strict();
|
|
35
|
+
const noFetch = "Slopify cannot reach the internet from here yet because it is still starting. Wait a moment, reload the page and try again.";
|
|
17
36
|
// The return type is inferred so Hono keeps the route types the SPA's client is
|
|
18
37
|
// generated from; see stagingRoutes.
|
|
19
38
|
export function providerRoutes(deps) {
|
|
@@ -29,7 +48,65 @@ export function providerRoutes(deps) {
|
|
|
29
48
|
now: () => deps.clock.now().getTime(),
|
|
30
49
|
report: (provider) => deps.log.write("warn", "model-catalog", { detail: `Could not load models for ${provider}` }),
|
|
31
50
|
});
|
|
51
|
+
const upkeep = () => ({ ...deps, uuid: deps.drafts?.uuid ?? randomUUID });
|
|
32
52
|
return (new Hono()
|
|
53
|
+
// The step lists Settings → Providers shows beside each key field.
|
|
54
|
+
.get("/key-guides", (c) => c.json({ guides: keyGuides }))
|
|
55
|
+
// What the first launch found, and the providers Play picks by default.
|
|
56
|
+
.get("/first-run", async (c) => {
|
|
57
|
+
c.header("Cache-Control", "no-store");
|
|
58
|
+
return c.json(await firstRunStatus({ db: deps.db, ...(deps.modelsFor === undefined ? {} : { modelsFor: deps.modelsFor }) }, await providerStatuses(readiness)));
|
|
59
|
+
})
|
|
60
|
+
.post("/first-run/dismiss", (c) => {
|
|
61
|
+
dismissFirstRun(deps.db);
|
|
62
|
+
return c.body(null, 204);
|
|
63
|
+
})
|
|
64
|
+
// "Check all": every CLI signed in, every key valid, every chosen model still offered.
|
|
65
|
+
.post("/health", async (c) => {
|
|
66
|
+
if (deps.fetch === undefined)
|
|
67
|
+
return problem(c, { status: 503, title: titleOf(503), detail: noFetch });
|
|
68
|
+
const catalogue = deps.catalogue;
|
|
69
|
+
return c.json(await checkProviderHealth({
|
|
70
|
+
...readiness,
|
|
71
|
+
clock: deps.clock,
|
|
72
|
+
fetch: deps.fetch,
|
|
73
|
+
probes: keyProbes,
|
|
74
|
+
...(catalogue === undefined ? {} : { catalogue: () => catalogue.read() }),
|
|
75
|
+
...(deps.modelsFor === undefined ? {} : { modelsFor: deps.modelsFor }),
|
|
76
|
+
...(deps.cliLogin === undefined ? {} : { login: deps.cliLogin }),
|
|
77
|
+
}));
|
|
78
|
+
})
|
|
79
|
+
// Checks for new, repriced and retired models now instead of waiting for the daily check.
|
|
80
|
+
.post("/catalogue/check", async (c) => {
|
|
81
|
+
if (!deps.catalogue?.sync)
|
|
82
|
+
return problem(c, {
|
|
83
|
+
status: 503,
|
|
84
|
+
title: titleOf(503),
|
|
85
|
+
detail: "The model list (models.yaml) is not loaded yet. Wait a moment and reload the page; if it stays like this, restart Slopify.",
|
|
86
|
+
});
|
|
87
|
+
await deps.catalogue.sync();
|
|
88
|
+
return c.json(deps.catalogue.status());
|
|
89
|
+
})
|
|
90
|
+
// Every template, schedule, draft and unfinished project still using a retired model.
|
|
91
|
+
.get("/catalogue/retired", (c) => {
|
|
92
|
+
c.header("Cache-Control", "no-store");
|
|
93
|
+
return c.json({
|
|
94
|
+
usages: deps.catalogue === undefined ? [] : retiredModelUsage(deps.db, deps.catalogue.read()),
|
|
95
|
+
});
|
|
96
|
+
})
|
|
97
|
+
.post("/catalogue/retired/switch", zValidator("json", switchBody, onInvalid), async (c) => {
|
|
98
|
+
if (deps.catalogue === undefined)
|
|
99
|
+
return problem(c, { status: 503, title: titleOf(503), detail: noFetch });
|
|
100
|
+
const result = await switchRetiredModel(upkeep(), deps.catalogue.read(), c.req.valid("json"));
|
|
101
|
+
if (!result.ok)
|
|
102
|
+
return problem(c, { status: 409, title: titleOf(409), detail: result.message });
|
|
103
|
+
return c.json(result);
|
|
104
|
+
})
|
|
105
|
+
.post("/catalogue/retired/switch-all", async (c) => {
|
|
106
|
+
if (deps.catalogue === undefined)
|
|
107
|
+
return problem(c, { status: 503, title: titleOf(503), detail: noFetch });
|
|
108
|
+
return c.json(await switchAllRetired(upkeep(), deps.catalogue.read()));
|
|
109
|
+
})
|
|
33
110
|
.get("/catalogue", (c) => c.json(deps.catalogue?.status() ?? {
|
|
34
111
|
warning: "The model list (models.yaml) is not loaded yet. Wait a moment and reload the page; if it stays like this, restart Slopify.",
|
|
35
112
|
}))
|
|
@@ -101,6 +178,15 @@ export function providerRoutes(deps) {
|
|
|
101
178
|
catalog.invalidate(id);
|
|
102
179
|
// The response says a key is stored and shows the mask, never the value.
|
|
103
180
|
return c.json(keyStatus(keys, id));
|
|
181
|
+
})
|
|
182
|
+
// The Test button: the cheapest read the provider offers, answered in plain words.
|
|
183
|
+
.post("/:id/key/test", zValidator("param", providerParam, onInvalid), async (c) => {
|
|
184
|
+
const { id } = c.req.valid("param");
|
|
185
|
+
if (providerById(id).auth === "cli")
|
|
186
|
+
return refusal(c, id, "cli-provider");
|
|
187
|
+
if (deps.fetch === undefined)
|
|
188
|
+
return problem(c, { status: 503, title: titleOf(503), detail: noFetch });
|
|
189
|
+
return c.json(await testProviderKey({ db: deps.db, clock: deps.clock, fetch: deps.fetch, probes: keyProbes }, id));
|
|
104
190
|
})
|
|
105
191
|
.delete("/:id/key", zValidator("param", providerParam, onInvalid), (c) => {
|
|
106
192
|
const { id } = c.req.valid("param");
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import { zValidator } from "@hono/zod-validator";
|
|
2
|
+
import { Hono } from "hono";
|
|
3
|
+
import { z } from "zod";
|
|
4
|
+
import { revisionControlSchema } from "../../slices/control/revision-control-schema.js";
|
|
5
|
+
import { recoverProject } from "../../slices/rebuild/recovery.js";
|
|
6
|
+
import { actOnVerdict, listVerdicts, verdictById } from "../../slices/reviews/repo.js";
|
|
7
|
+
import { latestReviews } from "../../slices/reviews/view.js";
|
|
8
|
+
import { currentRevisionId } from "../../slices/revisions/repo.js";
|
|
9
|
+
import { getRevisionView } from "../../slices/revisions/view.js";
|
|
10
|
+
import { onInvalid, problem, titleOf } from "./problem.js";
|
|
11
|
+
const id = z
|
|
12
|
+
.string()
|
|
13
|
+
.min(1)
|
|
14
|
+
.max(64)
|
|
15
|
+
.regex(/^[0-9A-Za-z_-]+$/);
|
|
16
|
+
const projectParam = z.object({ id });
|
|
17
|
+
const verdictParam = projectParam.extend({ verdictId: id });
|
|
18
|
+
// The project page's review column: each reviewed item's latest verdict with its reasons,
|
|
19
|
+
// Overrule to accept a failed item as it is, and Redo to have it made again through the
|
|
20
|
+
// same path as More → make it again in its section.
|
|
21
|
+
export function reviewRoutes(deps) {
|
|
22
|
+
const gone = (c) => problem(c, {
|
|
23
|
+
status: 404,
|
|
24
|
+
title: titleOf(404),
|
|
25
|
+
detail: "This review no longer exists; the project may have been deleted or restored from a backup. Reload the project page.",
|
|
26
|
+
});
|
|
27
|
+
const notify = (projectId) => {
|
|
28
|
+
try {
|
|
29
|
+
deps.hub.emit(projectId, { type: "project.updated", projectId });
|
|
30
|
+
}
|
|
31
|
+
catch (error) {
|
|
32
|
+
deps.log.write("error", "review.notify", {
|
|
33
|
+
projectId,
|
|
34
|
+
detail: error instanceof Error ? error.message : String(error),
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
return new Hono()
|
|
39
|
+
.get("/:id/reviews", zValidator("param", projectParam, onInvalid), (c) => {
|
|
40
|
+
const projectId = c.req.valid("param").id;
|
|
41
|
+
const head = currentRevisionId(deps.db, projectId);
|
|
42
|
+
const view = head === undefined ? undefined : getRevisionView(deps, projectId, head);
|
|
43
|
+
const outputs = (view?.outputs ?? [])
|
|
44
|
+
.filter((row) => row.selected)
|
|
45
|
+
.map((row) => ({
|
|
46
|
+
workKey: row.workKey,
|
|
47
|
+
fingerprint: row.fingerprint,
|
|
48
|
+
outputId: row.output.id,
|
|
49
|
+
}));
|
|
50
|
+
return c.json({ reviews: latestReviews(listVerdicts(deps.db, projectId), outputs) });
|
|
51
|
+
})
|
|
52
|
+
.post("/:id/reviews/:verdictId/overrule", zValidator("param", verdictParam, onInvalid), (c) => {
|
|
53
|
+
const { id: projectId, verdictId } = c.req.valid("param");
|
|
54
|
+
const result = actOnVerdict(deps.db, projectId, verdictId, "overruled", deps.clock.now().toISOString());
|
|
55
|
+
if (!result.ok)
|
|
56
|
+
return result.reason === "not-found"
|
|
57
|
+
? gone(c)
|
|
58
|
+
: problem(c, {
|
|
59
|
+
status: 409,
|
|
60
|
+
title: titleOf(409),
|
|
61
|
+
detail: "This verdict can't be overruled: it passed, or Slopify is already making the item again. Reload the project page to see where it is.",
|
|
62
|
+
extensions: { reason: "conflict" },
|
|
63
|
+
});
|
|
64
|
+
notify(projectId);
|
|
65
|
+
return c.json({ review: result.value });
|
|
66
|
+
})
|
|
67
|
+
.post("/:id/reviews/:verdictId/redo", zValidator("param", verdictParam, onInvalid), zValidator("json", revisionControlSchema, onInvalid), async (c) => {
|
|
68
|
+
const { id: projectId, verdictId } = c.req.valid("param");
|
|
69
|
+
const verdict = verdictById(deps.db, projectId, verdictId);
|
|
70
|
+
if (verdict === undefined)
|
|
71
|
+
return gone(c);
|
|
72
|
+
if (deps.rebuild === undefined)
|
|
73
|
+
return problem(c, {
|
|
74
|
+
status: 503,
|
|
75
|
+
title: titleOf(503),
|
|
76
|
+
detail: "Redo is not ready yet because Slopify is still starting. Wait a moment, reload the page and try again.",
|
|
77
|
+
});
|
|
78
|
+
if (verdict.redoState === "pending" || verdict.redoState === "started")
|
|
79
|
+
return problem(c, {
|
|
80
|
+
status: 409,
|
|
81
|
+
title: titleOf(409),
|
|
82
|
+
detail: "Slopify is already making this item again. Wait for it to land, then reload the project page.",
|
|
83
|
+
extensions: { reason: "conflict" },
|
|
84
|
+
});
|
|
85
|
+
const result = await recoverProject(deps.rebuild, projectId, {
|
|
86
|
+
...c.req.valid("json"),
|
|
87
|
+
action: { kind: "redo", item: verdict.itemKey },
|
|
88
|
+
});
|
|
89
|
+
if (!result.ok)
|
|
90
|
+
return refused(c, result, projectId);
|
|
91
|
+
actOnVerdict(deps.db, projectId, verdictId, "redone", deps.clock.now().toISOString());
|
|
92
|
+
notify(projectId);
|
|
93
|
+
return c.json(result, 202);
|
|
94
|
+
});
|
|
95
|
+
function refused(c, result, projectId) {
|
|
96
|
+
const status = result.reason === "no-project"
|
|
97
|
+
? 404
|
|
98
|
+
: result.reason === "invalid-selection" || result.reason === "invalid-edit"
|
|
99
|
+
? 400
|
|
100
|
+
: 409;
|
|
101
|
+
const lead = result.reason === "running"
|
|
102
|
+
? "This item, or work that depends on it, is still running. Wait for it to finish, or Pause the project, then use Redo again."
|
|
103
|
+
: result.reason === "readiness"
|
|
104
|
+
? "Slopify can't make this item again yet. Fix what is listed in Settings → Providers or Edit project, then use Redo again."
|
|
105
|
+
: result.reason === "conflict" || result.reason === "control-changed"
|
|
106
|
+
? "This project changed since the page loaded. Reload the project page, then use Redo again."
|
|
107
|
+
: result.reason === "invalid-selection"
|
|
108
|
+
? "This item can't be made again."
|
|
109
|
+
: "Slopify couldn't make this item again.";
|
|
110
|
+
return problem(c, {
|
|
111
|
+
status,
|
|
112
|
+
title: titleOf(status),
|
|
113
|
+
detail: [lead, ...(result.fields ?? []).map((row) => row.message)].join(" "),
|
|
114
|
+
extensions: {
|
|
115
|
+
reason: result.reason,
|
|
116
|
+
fields: result.fields ?? [],
|
|
117
|
+
currentRevisionId: currentRevisionId(deps.db, projectId) ?? null,
|
|
118
|
+
},
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
}
|
|
@@ -65,7 +65,7 @@ function unavailable(c, reason) {
|
|
|
65
65
|
status: 404,
|
|
66
66
|
title: titleOf(404),
|
|
67
67
|
detail: reason === "missing-file"
|
|
68
|
-
? "This file was deleted or moved from the project folder. In the Edit tab, use
|
|
68
|
+
? "This file was deleted or moved from the project folder. In the Edit tab, use Choose what to remake (in the project's More menu) to make it again."
|
|
69
69
|
: "This file is no longer part of the project. Reload the page to see its current files.",
|
|
70
70
|
extensions: { reason },
|
|
71
71
|
});
|
|
@@ -37,7 +37,7 @@ const details = {
|
|
|
37
37
|
conflict: "This project changed while you were editing. Reload the page to see the latest, then make your change again.",
|
|
38
38
|
"idempotency-conflict": "This change was already sent with different details. Reload the page, then try again.",
|
|
39
39
|
"invalid-edit": "Some of your changes are not valid. Fix the highlighted fields and save again.",
|
|
40
|
-
"stale-preview": "What needs rebuilding changed since you opened this review. Cancel it and open
|
|
40
|
+
"stale-preview": "What needs rebuilding changed since you opened this review. Cancel it and open Choose what to remake (in the project's More menu) again to see the latest.",
|
|
41
41
|
"invalid-selection": "Some of the outputs you picked can no longer be rebuilt. Reload the page and choose again.",
|
|
42
42
|
"review-required": 'This rebuild touches content you supplied yourself. Tick "Keep the provided content" for each item in the review, then choose Start rebuild.',
|
|
43
43
|
"cost-ack-required": "Some costs for this rebuild are unknown. Tick the box confirming you understand, then choose Start rebuild.",
|