@gentbajko/slopify 3.25.9 → 4.1.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/audio.js +1 -1
- package/dist/adapters/alignment/cache.js +4 -6
- package/dist/adapters/alignment/ctc.js +1 -2
- package/dist/adapters/alignment/lock.js +4 -3
- package/dist/adapters/alignment/multilingual.js +2 -4
- package/dist/adapters/alignment/prefetch.js +2 -1
- package/dist/adapters/alignment/runner.js +1 -1
- package/dist/adapters/alignment/worker.js +3 -2
- package/dist/adapters/explain.js +10 -12
- package/dist/adapters/ffmpeg.js +19 -1
- package/dist/adapters/host-cli/index.js +21 -4
- package/dist/adapters/host-cli/transport.js +2 -1
- package/dist/adapters/http-failure.js +53 -0
- package/dist/adapters/image/bytes.js +2 -1
- package/dist/adapters/image/codex.js +42 -9
- package/dist/adapters/image/fal.js +21 -53
- package/dist/adapters/image/google.js +33 -56
- package/dist/adapters/image/models.js +2 -2
- package/dist/adapters/image/openai.js +22 -48
- package/dist/adapters/image/reference.js +36 -0
- package/dist/adapters/image/replicate.js +21 -49
- package/dist/adapters/image/video.js +2 -1
- package/dist/adapters/interest/search-interest.js +113 -0
- package/dist/adapters/key-probes.js +54 -3
- package/dist/adapters/llm/antigravity-models.js +1 -1
- package/dist/adapters/llm/antigravity.js +11 -6
- 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 +52 -99
- package/dist/adapters/llm/claude-code.js +16 -4
- package/dist/adapters/llm/codex-limits.js +27 -68
- package/dist/adapters/llm/codex-models.js +1 -1
- package/dist/adapters/llm/codex-server-models.js +61 -109
- package/dist/adapters/llm/codex.js +12 -1
- package/dist/adapters/llm/document-workspace.js +3 -2
- package/dist/adapters/llm/image-workspace.js +3 -2
- package/dist/adapters/llm/openrouter.js +12 -48
- package/dist/adapters/llm/run-cli.js +9 -24
- 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 +21 -41
- package/dist/adapters/tts/inworld-async.js +6 -2
- package/dist/adapters/tts/inworld.js +6 -2
- package/dist/adapters/tts/openai.js +20 -50
- package/dist/adapters/tts/system.js +2 -1
- 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 +23 -14
- package/dist/catalog/schema.js +4 -1
- package/dist/catalog/store.js +39 -0
- package/dist/catalog/validate.js +5 -2
- 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 +11 -32
- package/dist/edge/http/actor.js +5 -3
- package/dist/edge/http/app.js +86 -11
- package/dist/edge/http/assistant.js +129 -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 +2 -4
- package/dist/edge/http/channel-memory.js +9 -22
- package/dist/edge/http/channels.js +14 -27
- package/dist/edge/http/checkpoints.js +39 -21
- 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 +5 -14
- package/dist/edge/http/draft-files.js +1 -3
- package/dist/edge/http/draft-problem.js +5 -10
- 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 +47 -14
- 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 +30 -26
- package/dist/edge/http/open-folder.js +5 -10
- package/dist/edge/http/outputs.js +7 -14
- package/dist/edge/http/patch-notes.js +6 -12
- package/dist/edge/http/planning.js +3 -7
- package/dist/edge/http/problem.js +2 -4
- package/dist/edge/http/project-create.js +2 -1
- package/dist/edge/http/project-templates.js +31 -19
- package/dist/edge/http/projects.js +24 -16
- package/dist/edge/http/prompts.js +9 -16
- package/dist/edge/http/pronunciations.js +1 -2
- package/dist/edge/http/providers.js +48 -34
- 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 +12 -25
- package/dist/edge/http/revision-files.js +6 -11
- package/dist/edge/http/revisions.js +14 -19
- package/dist/edge/http/run-cost.js +1 -2
- package/dist/edge/http/same-page.js +80 -0
- package/dist/edge/http/schedules.js +69 -40
- package/dist/edge/http/settings.js +27 -14
- package/dist/edge/http/social.js +283 -0
- package/dist/edge/http/staging.js +3 -5
- package/dist/edge/http/storage-files.js +5 -7
- package/dist/edge/http/storage.js +33 -25
- package/dist/edge/http/studio.js +94 -69
- package/dist/edge/http/style-preview.js +3 -6
- package/dist/edge/http/subtitles.js +3 -17
- package/dist/edge/http/template-studio.js +436 -0
- package/dist/edge/http/trash.js +11 -12
- 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 +3 -4
- 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 +29 -3
- package/dist/edge/mcp/http.js +11 -2
- package/dist/edge/mcp/instructions.js +6 -4
- package/dist/edge/mcp/library-tools.js +9 -5
- package/dist/edge/mcp/remake-tools.js +7 -4
- package/dist/edge/mcp/route-tool.js +35 -15
- package/dist/edge/mcp/server.js +13 -8
- package/dist/edge/mcp/tools-channels.js +11 -9
- package/dist/edge/mcp/tools-designs.js +165 -0
- package/dist/edge/mcp/tools-library.js +19 -7
- package/dist/edge/mcp/tools-planning.js +40 -21
- package/dist/edge/mcp/tools-projects.js +38 -11
- package/dist/edge/mcp/tools-publishing.js +9 -9
- package/dist/edge/mcp/tools-runs.js +23 -10
- package/dist/edge/mcp/tools-schedules.js +32 -8
- package/dist/edge/mcp/tools-setup.js +75 -11
- package/dist/edge/mcp/tools-social.js +47 -0
- package/dist/edge/mcp/tools-storage.js +11 -3
- 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/migrations/0078-uploaded-from-links.sql +7 -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/pipeline.js +12 -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/ports/settings-places.js +31 -0
- package/dist/kernel/ports/system-speech.js +31 -3
- package/dist/kernel/runner/attempt.js +14 -4
- 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 +18 -12
- package/dist/kernel/runner/standalone.js +11 -7
- package/dist/main.js +338 -21
- package/dist/model-catalog.js +20 -0
- package/dist/model-checks.js +166 -0
- package/dist/patch-notes/4.0.0.md +51 -0
- package/dist/patch-notes/4.1.0.md +42 -0
- package/dist/patch-notes/index.json +12 -0
- package/dist/slices/admission/heads.js +2 -1
- package/dist/slices/admission/listing-facts.js +95 -0
- package/dist/slices/admission/repo.js +11 -7
- package/dist/slices/admission/rules.js +50 -15
- 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/state-words.js +34 -5
- 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 +86 -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 +344 -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 +8 -7
- package/dist/slices/channels/cast-images.js +3 -2
- 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 +7 -1
- package/dist/slices/channels/service.js +10 -0
- package/dist/slices/channels/videos.js +3 -15
- package/dist/slices/checkpoints/index.js +0 -1
- package/dist/slices/control/providers.js +2 -28
- 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 +130 -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 +223 -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/document/model.js +7 -4
- package/dist/slices/document/render.js +2 -1
- package/dist/slices/document/theme-schema.js +1 -1
- package/dist/slices/document/theme.js +49 -3
- package/dist/slices/episodes/summarize.js +6 -2
- package/dist/slices/estimate/index.js +72 -28
- package/dist/slices/estimate/requests.js +4 -4
- package/dist/slices/fixes/rules.js +3 -1
- package/dist/slices/fonts/catalog.js +5 -3
- package/dist/slices/fonts/coverage.js +2 -1
- 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 +8 -16
- package/dist/slices/images/scenes.js +102 -28
- package/dist/slices/keywords/digest.js +85 -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 +5 -4
- package/dist/slices/library/used-by.js +25 -10
- package/dist/slices/loudness/loudnorm.js +53 -12
- package/dist/slices/loudness/model.js +3 -2
- package/dist/slices/loudness/sentence-level.js +2 -1
- 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 +125 -0
- package/dist/slices/model-checks/run.js +143 -0
- package/dist/slices/model-checks/service.js +198 -0
- package/dist/slices/model-upkeep/model.js +1 -1
- package/dist/slices/model-upkeep/switch.js +72 -27
- package/dist/slices/model-upkeep/usage.js +39 -13
- package/dist/slices/narration/breaths.js +3 -1
- package/dist/slices/narration/clean-pieces.js +3 -1
- package/dist/slices/narration/concat.js +2 -1
- package/dist/slices/narration/pauses-model.js +3 -2
- package/dist/slices/narration/peaks.js +3 -4
- package/dist/slices/narration/pronunciation-chunks.js +4 -3
- package/dist/slices/narration/pronunciation.js +6 -4
- 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 +4 -3
- package/dist/slices/onboarding/copy.js +21 -3
- package/dist/slices/onboarding/first-run.js +16 -40
- package/dist/slices/onboarding/full-video.js +3 -2
- package/dist/slices/onboarding/install.js +6 -4
- package/dist/slices/onboarding/model.js +26 -37
- package/dist/slices/onboarding/packs.js +22 -22
- package/dist/slices/onboarding/preferred-model.js +14 -0
- package/dist/slices/onboarding/quick-short.js +105 -65
- package/dist/slices/onboarding/sample.js +4 -2
- package/dist/slices/onboarding/state.js +9 -11
- package/dist/slices/onboarding/template.js +8 -6
- package/dist/slices/outputs/add.js +3 -2
- package/dist/slices/outputs/service.js +2 -2
- package/dist/slices/patch-notes/library.js +4 -3
- package/dist/slices/play-drafts/convert.js +4 -0
- package/dist/slices/play-drafts/readiness.js +3 -3
- package/dist/slices/play-drafts/review-inputs.js +9 -3
- package/dist/slices/play-drafts/review.js +5 -4
- package/dist/slices/play-drafts/schema.js +4 -0
- package/dist/slices/play-drafts/service.js +17 -7
- package/dist/slices/project-templates/from-project.js +9 -2
- package/dist/slices/project-templates/next-chapter.js +2 -1
- package/dist/slices/project-templates/provider-defaults.js +22 -0
- package/dist/slices/project-templates/repo.js +6 -1
- package/dist/slices/project-templates/setup-sections.js +26 -0
- package/dist/slices/project-templates/setup.js +14 -1
- 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 +32 -14
- 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 +4 -4
- 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 +8 -5
- 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 +102 -31
- package/dist/slices/rebuild/recipe-visual.js +55 -16
- package/dist/slices/rebuild/recipe-voices.js +3 -2
- package/dist/slices/rebuild/recipe-work.js +19 -7
- package/dist/slices/rebuild/recipe-youtube.js +4 -3
- package/dist/slices/rebuild/recovery-selection.js +3 -1
- package/dist/slices/rebuild/recovery.js +9 -8
- package/dist/slices/rebuild/remake-outdated.js +5 -5
- package/dist/slices/rebuild/repo.js +4 -3
- package/dist/slices/rebuild/review-promise.js +25 -0
- package/dist/slices/rebuild/review-redo.js +3 -2
- 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 +10 -9
- package/dist/slices/rebuild/runtime-article.js +7 -5
- package/dist/slices/rebuild/runtime-checkpoints.js +5 -4
- package/dist/slices/rebuild/runtime-document.js +11 -13
- package/dist/slices/rebuild/runtime-export-bed.js +8 -12
- package/dist/slices/rebuild/runtime-export-edit.js +8 -16
- package/dist/slices/rebuild/runtime-export-inputs.js +12 -11
- package/dist/slices/rebuild/runtime-export-short.js +9 -5
- package/dist/slices/rebuild/runtime-export.js +38 -15
- package/dist/slices/rebuild/runtime-figure-card.js +2 -1
- package/dist/slices/rebuild/runtime-image.js +8 -12
- 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 +17 -22
- 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 +13 -17
- 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 +67 -25
- package/dist/slices/rebuild/runtime-publication.js +10 -9
- package/dist/slices/rebuild/runtime-review.js +39 -33
- package/dist/slices/rebuild/runtime-run.js +4 -1
- package/dist/slices/rebuild/runtime-shorts.js +31 -50
- package/dist/slices/rebuild/runtime-store.js +20 -10
- package/dist/slices/rebuild/runtime-subtitles.js +28 -25
- package/dist/slices/rebuild/runtime-voices.js +10 -16
- package/dist/slices/rebuild/runtime-youtube.js +14 -31
- package/dist/slices/rebuild/service-readiness.js +11 -3
- 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/research/synthesis.js +3 -2
- package/dist/slices/reviews/external-model.js +4 -1
- package/dist/slices/reviews/external-repo.js +2 -1
- package/dist/slices/reviews/external-rows.js +2 -3
- package/dist/slices/reviews/holds.js +5 -4
- package/dist/slices/reviews/model.js +32 -11
- package/dist/slices/reviews/repo.js +2 -1
- package/dist/slices/reviews/rules.js +4 -3
- package/dist/slices/reviews/verdict.js +2 -1
- package/dist/slices/reviews/view.js +5 -2
- package/dist/slices/revisions/downloads.js +4 -5
- package/dist/slices/revisions/image-plan.js +1 -1
- package/dist/slices/revisions/mutation-assets.js +9 -5
- package/dist/slices/revisions/mutation-cues.js +2 -5
- package/dist/slices/revisions/mutations.js +3 -3
- 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 +18 -7
- 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 +30 -21
- package/dist/slices/schedules/rescore.js +35 -0
- package/dist/slices/schedules/scheduler.js +13 -3
- package/dist/slices/schedules/schema.js +57 -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 +53 -41
- package/dist/slices/settings/cli-paths.js +4 -6
- package/dist/slices/settings/first-run.js +8 -43
- package/dist/slices/settings/health.js +30 -15
- package/dist/slices/settings/key-test.js +1 -1
- package/dist/slices/settings/model-reach.js +3 -17
- package/dist/slices/settings/model.js +14 -2
- package/dist/slices/settings/playback.js +34 -23
- 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/keep.js +2 -1
- package/dist/slices/shorts/pick.js +6 -18
- package/dist/slices/shorts/prompts.js +4 -14
- package/dist/slices/shorts/render.js +6 -2
- 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 +132 -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 +56 -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 +129 -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 +22 -4
- 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 +17 -12
- 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 +26 -9
- package/dist/slices/studio/calendar.js +7 -2
- package/dist/slices/studio/disclosure.js +4 -4
- package/dist/slices/studio/model.js +1 -1
- package/dist/slices/studio/pack.js +9 -8
- package/dist/slices/studio/pick.js +2 -11
- package/dist/slices/studio/plan.js +7 -16
- package/dist/slices/studio/queue.js +3 -14
- package/dist/slices/studio/releases.js +26 -0
- package/dist/slices/studio/settings.js +20 -57
- package/dist/slices/studio/videos.js +35 -4
- package/dist/slices/style-preview/render.js +37 -16
- package/dist/slices/style-preview/schema.js +14 -1
- package/dist/slices/style-preview/service.js +2 -1
- package/dist/slices/style-preview/settings.js +33 -5
- package/dist/slices/subtitles/captions.js +1 -1
- 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 +161 -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 +106 -0
- package/dist/slices/template-studio/cards.js +78 -0
- package/dist/slices/template-studio/checks.js +322 -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 +58 -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 +207 -0
- package/dist/slices/template-studio/paid-view.js +66 -0
- package/dist/slices/template-studio/paid.js +355 -0
- package/dist/slices/template-studio/quote.js +155 -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 +32 -0
- package/dist/slices/template-studio/schedule-sample.js +91 -0
- package/dist/slices/template-studio/schema.js +243 -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 +175 -0
- package/dist/slices/template-studio/status.js +191 -0
- package/dist/slices/template-studio/topic.js +223 -0
- package/dist/slices/thumbnail/by-llm.js +1 -1
- 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/uploads/repo.js +37 -1
- package/dist/slices/uploads/rules.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/edit-list.js +2 -2
- package/dist/slices/video/edit-settings.js +1 -1
- package/dist/slices/video/encoding.js +169 -0
- package/dist/slices/video/ffmpeg.js +23 -21
- package/dist/slices/video/figure-card.js +1 -15
- package/dist/slices/video/plan.js +2 -1
- package/dist/slices/video/slideshow.js +43 -18
- package/dist/slices/voices/attribution.js +3 -2
- package/dist/slices/voices/model.js +9 -8
- package/dist/slices/voices/palette.js +0 -3
- package/dist/slices/voices/script.js +2 -1
- package/dist/slices/youtube/answer.js +4 -16
- package/dist/slices/youtube/edits-repo.js +3 -12
- package/dist/tutorials/AI-CLIs.md +25 -25
- package/dist/tutorials/Agents-MCP.md +34 -33
- package/dist/tutorials/Assistant.md +42 -0
- package/dist/tutorials/Backups.md +7 -7
- package/dist/tutorials/Calendar.md +17 -25
- package/dist/tutorials/Cast-Library.md +19 -19
- package/dist/tutorials/Channels.md +42 -35
- package/dist/tutorials/Costs-and-Run-Cost.md +38 -40
- package/dist/tutorials/Create-Article.md +114 -0
- package/dist/tutorials/Create-Extras.md +112 -0
- package/dist/tutorials/Create-Images.md +165 -0
- package/dist/tutorials/Create-Narration.md +178 -0
- package/dist/tutorials/Create-Overview.md +244 -0
- package/dist/tutorials/Create-Video-and-Style.md +144 -0
- package/dist/tutorials/Docker.md +3 -3
- package/dist/tutorials/Document-Themes.md +32 -32
- package/dist/tutorials/Editing-a-Project.md +94 -94
- package/dist/tutorials/FAQ.md +18 -18
- package/dist/tutorials/First-Launch-and-Welcome.md +50 -64
- package/dist/tutorials/Home-and-Projects.md +115 -71
- package/dist/tutorials/Home.md +33 -33
- package/dist/tutorials/Install.md +14 -13
- package/dist/tutorials/Intros-and-Outros.md +33 -34
- package/dist/tutorials/Keyboard-Shortcuts-and-Command-Palette.md +62 -58
- package/dist/tutorials/Keywords-Slopify-Fills.md +87 -0
- package/dist/tutorials/Models.md +53 -21
- package/dist/tutorials/Multiple-Voices.md +41 -40
- package/dist/tutorials/Narration-Aliases-and-Glossary.md +14 -14
- package/dist/tutorials/Notifications.md +27 -17
- package/dist/tutorials/Other-Languages.md +15 -15
- package/dist/tutorials/PDF-Documents.md +38 -29
- package/dist/tutorials/Posting-to-Social-Sites.md +121 -0
- package/dist/tutorials/Project-Page.md +118 -120
- package/dist/tutorials/Prompts-and-Templates.md +84 -0
- package/dist/tutorials/Prompts.md +48 -44
- package/dist/tutorials/Providers-and-Keys.md +105 -98
- package/dist/tutorials/Publishing-to-YouTube.md +31 -31
- package/dist/tutorials/Recovery-and-Retries.md +31 -31
- package/dist/tutorials/Remote-Access.md +63 -0
- package/dist/tutorials/Reviews-and-Checkpoints.md +98 -90
- package/dist/tutorials/Schedules.md +46 -27
- package/dist/tutorials/Settings-Reference.md +194 -174
- package/dist/tutorials/Shorts.md +34 -33
- package/dist/tutorials/Start-at-Login.md +3 -3
- package/dist/tutorials/Studio-Extension.md +22 -24
- package/dist/tutorials/Template-Studio.md +128 -0
- package/dist/tutorials/Templates.md +51 -77
- package/dist/tutorials/Trash.md +4 -4
- package/dist/tutorials/Troubleshooting.md +44 -44
- package/dist/tutorials/Uninstalling-and-Moving.md +5 -5
- package/dist/tutorials/Updating-and-Patch-Notes.md +18 -18
- package/dist/tutorials/Video-Editing.md +34 -34
- package/dist/tutorials/Where-Your-Files-Live.md +10 -10
- package/dist/tutorials/YouTube-Description.md +26 -26
- package/dist/tutorials/Your-First-Short.md +35 -35
- package/dist/tutorials/_Sidebar.md +19 -14
- package/dist/updater/work-in-progress.js +42 -12
- package/dist/web/assets/index-BNftKQrA.css +1 -0
- package/dist/web/assets/index-Cpa58woP.js +459 -0
- package/dist/web/assets/pdf-BKh8f7R6.js +56 -0
- package/dist/web/assets/pdf.worker.min-BmVo14Nb.mjs +28 -0
- package/dist/web/assets/pdf.worker.min-D7i8o3wR.js +1 -0
- package/dist/web/index.html +2 -2
- package/package.json +6 -2
- 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/tutorials/Library-Overview.md +0 -76
- package/dist/tutorials/Play-Images.md +0 -161
- package/dist/tutorials/Play-Narration.md +0 -167
- package/dist/tutorials/Play-Outputs.md +0 -109
- package/dist/tutorials/Play-Overview.md +0 -227
- package/dist/tutorials/Play-Title-and-Article.md +0 -136
- package/dist/tutorials/Play-Video-and-Style.md +0 -143
- package/dist/web/assets/index-BadsoORk.css +0 -1
- package/dist/web/assets/index-D8LqNsCl.js +0 -326
- package/dist/web/assets/pdf-T7MCwjIv.js +0 -54
- package/dist/web/assets/pdf.worker.min-Dswkl-cV.mjs +0 -29
- package/dist/web/assets/pdf.worker.min-zC6nfbxG.js +0 -1
|
@@ -1,14 +1,18 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { transact } from "../../kernel/db/tx.js";
|
|
3
|
+
import { errorText } from "../../kernel/errors.js";
|
|
4
|
+
import { jsonBetween } from "../../kernel/json-answer.js";
|
|
3
5
|
import { liveProject } from "../admission/repo.js";
|
|
4
6
|
import { defaultChannelId } from "../channels/model.js";
|
|
5
7
|
import { projectChannels, scheduleChannel } from "../channels/repo.js";
|
|
6
8
|
import { channelVideoTitles } from "../channels/videos.js";
|
|
7
|
-
import {
|
|
9
|
+
import { readJsonSetting, writeSetting } from "../settings/repo.js";
|
|
10
|
+
import { notTemplateSample } from "../template-studio/repo.js";
|
|
8
11
|
import { scheduleById } from "./repo.js";
|
|
9
12
|
import { heldTopicEditSchema, heldTopicSchema, queueMax, topicMoveSchema, topicQueueSchema, topicTitleSchema, topicTransferSchema, } from "./schema.js";
|
|
10
13
|
import { newTopics } from "./similar.js";
|
|
11
|
-
import {
|
|
14
|
+
import { scheduleKeywords, topicRowProblems } from "./topic-list.js";
|
|
15
|
+
import { pinTopic, rankedItems, saveScores, scoreTopics } from "./topic-scores.js";
|
|
12
16
|
// A schedule that finds its own topics asks an LLM whenever its queue holds fewer than
|
|
13
17
|
// `keepAtLeast`. Only one generation per schedule runs at a time: a lease on the schedule row
|
|
14
18
|
// (`topics_generating_at`) is taken before the LLM is asked and cleared after, so a tick and a
|
|
@@ -104,9 +108,9 @@ async function ask(deps, schedule, options) {
|
|
|
104
108
|
const template = deps.template(schedule.templateId);
|
|
105
109
|
const llm = schedule.topicGeneration.llm ?? template?.document.form.llm;
|
|
106
110
|
if (llm === undefined)
|
|
107
|
-
return fail("the template this schedule uses was deleted, so there is no
|
|
111
|
+
return fail("the template this schedule uses was deleted, so there is no AI model to ask. Edit the schedule and choose a template, or pick a Service and Model under Topic generation.");
|
|
108
112
|
if (deps.topicLlm === undefined)
|
|
109
|
-
return fail("no
|
|
113
|
+
return fail("no AI model is available in this Slopify. Restart Slopify and try again.");
|
|
110
114
|
const wanted = Math.max(generationWanted(schedule), options.force ? spare : 0);
|
|
111
115
|
const known = knownTitles(deps, schedule.id);
|
|
112
116
|
const signal = AbortSignal.any([
|
|
@@ -129,26 +133,40 @@ async function ask(deps, schedule, options) {
|
|
|
129
133
|
catch (error) {
|
|
130
134
|
if (options.signal?.aborted === true)
|
|
131
135
|
return { ok: false, reason: "failed", message: "Slopify was closing." };
|
|
132
|
-
const detail =
|
|
133
|
-
return fail(`${llm.provider} (${llm.model}) did not answer: ${detail.trim().slice(0, 300)}. Check it in Settings →
|
|
136
|
+
const detail = errorText(error);
|
|
137
|
+
return fail(`${llm.provider} (${llm.model}) did not answer: ${detail.trim().slice(0, 300)}. Check it in Settings → Keys & services, or pick another Service or Model under Edit → Topic generation.`);
|
|
134
138
|
}
|
|
135
139
|
const suggested = parseTopics(answer);
|
|
136
140
|
if (suggested.length === 0)
|
|
137
|
-
return fail(`${llm.provider} (${llm.model}) answered without a list of topics. Try again, or pick another
|
|
141
|
+
return fail(`${llm.provider} (${llm.model}) answered without a list of topics. Try again, or pick another Service or Model under Edit → Topic generation.`);
|
|
142
|
+
// Scored outside the transaction: search may take seconds, and never holds the database.
|
|
143
|
+
const scores = schedule.topicScoring === "off"
|
|
144
|
+
? undefined
|
|
145
|
+
: await scoreTopics(deps, schedule.topicScoring, newTopics(suggested, known), known, template?.document.form.language ?? "en", options.signal);
|
|
138
146
|
return transact(deps.db, () => {
|
|
139
147
|
const current = scheduleById(deps.db, schedule.id);
|
|
140
148
|
if (current === undefined || current.deletedAt !== null)
|
|
141
149
|
return { ok: false, reason: "not-found", message: "This schedule no longer exists." };
|
|
142
150
|
// Checked again against what is there now: a topic added while the LLM was thinking counts.
|
|
143
|
-
const
|
|
151
|
+
const candidates = newTopics(suggested, knownTitles(deps, schedule.id));
|
|
152
|
+
// With scoring, the best-scored are kept; without, the LLM's own order, as before.
|
|
153
|
+
const fresh = (scores === undefined
|
|
154
|
+
? candidates
|
|
155
|
+
: candidates.toSorted((a, b) => (scores.get(b)?.score ?? -1) - (scores.get(a)?.score ?? -1))).slice(0, Math.max(0, Math.min(wanted, queueMax - current.items.length)));
|
|
144
156
|
if (fresh.length === 0)
|
|
145
|
-
return fail("every topic the
|
|
157
|
+
return fail("every topic the AI model suggested was already made, queued or turned down for this schedule. Add more detail to the series brief under Edit, so it has new ground to cover.");
|
|
146
158
|
const at = deps.clock.now().toISOString();
|
|
147
159
|
const mode = current.topicGeneration.mode === "hold" ? "hold" : "queue";
|
|
160
|
+
if (scores !== undefined)
|
|
161
|
+
saveScores(deps.db, schedule.id, new Map(fresh.flatMap((title) => {
|
|
162
|
+
const score = scores.get(title);
|
|
163
|
+
return score === undefined ? [] : [[title, score]];
|
|
164
|
+
})), at);
|
|
148
165
|
if (mode === "queue") {
|
|
166
|
+
const items = [...current.items, ...fresh.map((title) => ({ title, values: {} }))];
|
|
149
167
|
deps.db
|
|
150
168
|
.prepare("UPDATE schedules SET items_json=?,version=version+1,updated_at=? WHERE id=? AND deleted_at IS NULL")
|
|
151
|
-
.run(JSON.stringify(
|
|
169
|
+
.run(JSON.stringify(scores === undefined ? items : rankedItems(deps.db, schedule.id, items)), at, schedule.id);
|
|
152
170
|
}
|
|
153
171
|
else {
|
|
154
172
|
const start = nextRank(deps, schedule.id);
|
|
@@ -158,6 +176,8 @@ async function ask(deps, schedule, options) {
|
|
|
158
176
|
VALUES (?,?,?,'held',?,?)`)
|
|
159
177
|
.run(deps.uuid(), schedule.id, title, start + index, at);
|
|
160
178
|
});
|
|
179
|
+
if (scores !== undefined)
|
|
180
|
+
rankHeld(deps, schedule.id);
|
|
161
181
|
}
|
|
162
182
|
deps.db
|
|
163
183
|
.prepare("UPDATE schedules SET topics_generated_at=?,topics_failed_at=NULL,topics_error=NULL WHERE id=?")
|
|
@@ -180,6 +200,17 @@ async function ask(deps, schedule, options) {
|
|
|
180
200
|
return { ok: true, added: fresh.length, mode };
|
|
181
201
|
});
|
|
182
202
|
}
|
|
203
|
+
// Held topics in score order: unscored ones first as they were, then the scored, best first.
|
|
204
|
+
export function rankHeld(deps, scheduleId) {
|
|
205
|
+
const held = deps.db
|
|
206
|
+
.prepare("SELECT id,title FROM schedule_topics WHERE schedule_id=? AND state='held' ORDER BY rank,created_at,id")
|
|
207
|
+
.all(scheduleId)
|
|
208
|
+
.map((row) => z.object({ id: z.string(), title: z.string() }).parse(row));
|
|
209
|
+
const rank = deps.db.prepare("UPDATE schedule_topics SET rank=? WHERE id=?");
|
|
210
|
+
rankedItems(deps.db, scheduleId, held).forEach((topic, index) => {
|
|
211
|
+
rank.run(index, topic.id);
|
|
212
|
+
});
|
|
213
|
+
}
|
|
183
214
|
function nextRank(deps, scheduleId) {
|
|
184
215
|
const row = deps.db
|
|
185
216
|
.prepare("SELECT max(rank) AS rank FROM schedule_topics WHERE schedule_id=? AND state='held'")
|
|
@@ -200,7 +231,8 @@ export function knownTitles(deps, scheduleId) {
|
|
|
200
231
|
const channel = scheduleChannel(deps.db, scheduleId);
|
|
201
232
|
const channels = projectChannels(deps.db);
|
|
202
233
|
const projects = deps.db
|
|
203
|
-
|
|
234
|
+
// A template's studio sample is no video of the channel's.
|
|
235
|
+
.prepare(`SELECT id,title FROM projects WHERE ${liveProject()} AND ${notTemplateSample()} ORDER BY created_at DESC`)
|
|
204
236
|
.all()
|
|
205
237
|
.map((row) => z.object({ id: z.string(), title: z.string() }).parse(row))
|
|
206
238
|
.filter((row) => channel === undefined || (channels.get(row.id) ?? defaultChannelId) === channel.id)
|
|
@@ -254,20 +286,8 @@ export function topicMessages(schedule, templateTitle, known, count) {
|
|
|
254
286
|
}
|
|
255
287
|
// A JSON array of strings, or failing that one topic per line with bullets and numbers dropped.
|
|
256
288
|
export function parseTopics(answer) {
|
|
257
|
-
const
|
|
258
|
-
const
|
|
259
|
-
let lines;
|
|
260
|
-
if (start !== -1 && end > start) {
|
|
261
|
-
try {
|
|
262
|
-
const parsed = JSON.parse(answer.slice(start, end + 1));
|
|
263
|
-
if (Array.isArray(parsed))
|
|
264
|
-
lines = parsed;
|
|
265
|
-
}
|
|
266
|
-
catch {
|
|
267
|
-
lines = undefined;
|
|
268
|
-
}
|
|
269
|
-
}
|
|
270
|
-
lines ??= answer.split(/\r?\n/);
|
|
289
|
+
const parsed = jsonBetween(answer, "[", "]");
|
|
290
|
+
const lines = Array.isArray(parsed) ? parsed : answer.split(/\r?\n/);
|
|
271
291
|
return lines
|
|
272
292
|
.map((line) => typeof line === "string"
|
|
273
293
|
? line
|
|
@@ -286,16 +306,7 @@ export function parseTopics(answer) {
|
|
|
286
306
|
export const heldValuesKey = "schedules.held-topic-values";
|
|
287
307
|
const heldValuesSchema = z.record(z.string(), z.record(z.string(), z.string()));
|
|
288
308
|
function readHeldValues(db) {
|
|
289
|
-
|
|
290
|
-
if (raw === undefined)
|
|
291
|
-
return {};
|
|
292
|
-
try {
|
|
293
|
-
const parsed = heldValuesSchema.safeParse(JSON.parse(raw));
|
|
294
|
-
return parsed.success ? parsed.data : {};
|
|
295
|
-
}
|
|
296
|
-
catch {
|
|
297
|
-
return {};
|
|
298
|
-
}
|
|
309
|
+
return readJsonSetting(db, heldValuesKey, heldValuesSchema, {});
|
|
299
310
|
}
|
|
300
311
|
// Entries of topics that are no longer held (approved, turned down, or gone with their
|
|
301
312
|
// schedule) are dropped on every write.
|
|
@@ -394,11 +405,11 @@ export function editHeldTopic(deps, scheduleId, topicId, input) {
|
|
|
394
405
|
const schedule = scheduleById(deps.db, scheduleId);
|
|
395
406
|
if (schedule === undefined)
|
|
396
407
|
return { ok: false, reason: "not-found" };
|
|
397
|
-
const
|
|
398
|
-
if (
|
|
408
|
+
const known = scheduleKeywords(deps, schedule.templateId);
|
|
409
|
+
if (known === undefined)
|
|
399
410
|
return { ok: false, reason: "missing-template" };
|
|
400
411
|
const problems = topicRowProblems([{ title: parsed.data, values }], {
|
|
401
|
-
keywords:
|
|
412
|
+
keywords: known.filter((name) => name !== schedule.topicKeyword),
|
|
402
413
|
topicKeyword: schedule.topicKeyword,
|
|
403
414
|
});
|
|
404
415
|
if (problems.length > 0)
|
|
@@ -447,6 +458,7 @@ export function moveTopic(deps, scheduleId, input) {
|
|
|
447
458
|
return { ok: false, reason: "invalid-input" };
|
|
448
459
|
items.splice(Math.min(parsed.data.to, items.length), 0, topic);
|
|
449
460
|
writeItems(deps, scheduleId, schedule.version, items);
|
|
461
|
+
pinTopic(deps.db, scheduleId, topic.title);
|
|
450
462
|
return { ok: true, value: scheduleById(deps.db, scheduleId) };
|
|
451
463
|
});
|
|
452
464
|
}
|
|
@@ -463,11 +475,11 @@ export function replaceTopics(deps, scheduleId, input) {
|
|
|
463
475
|
const schedule = open.value;
|
|
464
476
|
if (schedule.version !== parsed.data.baseVersion)
|
|
465
477
|
return { ok: false, reason: "conflict" };
|
|
466
|
-
const
|
|
467
|
-
if (
|
|
478
|
+
const keywords = scheduleKeywords(deps, schedule.templateId);
|
|
479
|
+
if (keywords === undefined)
|
|
468
480
|
return { ok: false, reason: "missing-template" };
|
|
469
481
|
const problems = topicRowProblems(parsed.data.items, {
|
|
470
|
-
keywords
|
|
482
|
+
keywords,
|
|
471
483
|
topicKeyword: schedule.topicKeyword,
|
|
472
484
|
kept: schedule.items,
|
|
473
485
|
});
|
|
@@ -2,9 +2,10 @@ import { constants } from "node:fs";
|
|
|
2
2
|
import { access, stat } from "node:fs/promises";
|
|
3
3
|
import { extname, isAbsolute } from "node:path";
|
|
4
4
|
import { z } from "zod";
|
|
5
|
+
import { isUnreachablePath } from "../../kernel/errors.js";
|
|
5
6
|
import { cliProbeTimeoutMs, readinessFromProbe } from "./cli-status.js";
|
|
6
7
|
import { providerById } from "./model.js";
|
|
7
|
-
import {
|
|
8
|
+
import { readJsonSetting, writeSetting } from "./repo.js";
|
|
8
9
|
export const cliPathMaxLength = 4096;
|
|
9
10
|
const pathSchema = z
|
|
10
11
|
.string()
|
|
@@ -20,8 +21,7 @@ export function cliPathStatus(db, id) {
|
|
|
20
21
|
const provider = providerById(id);
|
|
21
22
|
if (provider.auth !== "cli")
|
|
22
23
|
throw new Error(`${id} is not a CLI provider`);
|
|
23
|
-
const
|
|
24
|
-
const configured = stored === undefined ? null : storedPathSchema.parse(JSON.parse(stored));
|
|
24
|
+
const configured = readJsonSetting(db, `cli.path.${installationId(id)}`, storedPathSchema, null);
|
|
25
25
|
return { configured, command: configured ?? provider.binary };
|
|
26
26
|
}
|
|
27
27
|
// Read on each attempt, so a saved path reaches the next invocation without a restart.
|
|
@@ -82,9 +82,7 @@ async function save(deps, id, raw) {
|
|
|
82
82
|
: constants.X_OK);
|
|
83
83
|
}
|
|
84
84
|
catch (error) {
|
|
85
|
-
if (error
|
|
86
|
-
"code" in error &&
|
|
87
|
-
["ENOENT", "ENOTDIR", "EACCES", "EPERM", "ELOOP"].includes(String(error.code)))
|
|
85
|
+
if (isUnreachablePath(error))
|
|
88
86
|
return {
|
|
89
87
|
ok: false,
|
|
90
88
|
message: "Slopify cannot find or run a program at this path. Check the path is correct and the file is allowed to run.",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { readinessIsUsable } from "../../kernel/ports/model.js";
|
|
3
|
-
import { keyedProviders, readSetting, writeSetting } from "./repo.js";
|
|
3
|
+
import { keyedProviders, readJsonSetting, readSetting, writeSetting } from "./repo.js";
|
|
4
4
|
// What a fresh Play form picks before the user picks anything. Written once, on the first
|
|
5
5
|
// launch that finds a command-line tool, and read by Play for every new draft.
|
|
6
6
|
const choiceSchema = z.object({ provider: z.string().min(1), model: z.string() }).strict();
|
|
@@ -9,26 +9,17 @@ export const providerDefaultsSchema = z
|
|
|
9
9
|
.strict();
|
|
10
10
|
export const providerDefaultsKey = "provider.defaults";
|
|
11
11
|
const defaultsKey = providerDefaultsKey;
|
|
12
|
+
// Set by the first-launch callout's "Got it" in older versions. Nothing writes it now; an
|
|
13
|
+
// install that pressed it keeps its first launch behind it.
|
|
12
14
|
const doneKey = "first-run.done";
|
|
13
15
|
// The order a text tool is preferred in when several are found.
|
|
14
16
|
const textPreference = ["claude-code", "codex", "gemini"];
|
|
15
17
|
export function readProviderDefaults(db) {
|
|
16
|
-
|
|
17
|
-
if (raw === undefined)
|
|
18
|
-
return {};
|
|
19
|
-
try {
|
|
20
|
-
const parsed = providerDefaultsSchema.safeParse(JSON.parse(raw));
|
|
21
|
-
return parsed.success ? parsed.data : {};
|
|
22
|
-
}
|
|
23
|
-
catch {
|
|
24
|
-
return {};
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
export function dismissFirstRun(db) {
|
|
28
|
-
writeSetting(db, doneKey, "1");
|
|
18
|
+
return readJsonSetting(db, defaultsKey, providerDefaultsSchema, {});
|
|
29
19
|
}
|
|
30
|
-
// First launch: find Claude Code, Codex and Gemini
|
|
31
|
-
//
|
|
20
|
+
// First launch: find Claude Code, Codex and Gemini and pick the found ones as Create's defaults.
|
|
21
|
+
// Later launches only report what was found. The first-run screen (slices/onboarding) says what
|
|
22
|
+
// was found in its own words.
|
|
32
23
|
export async function firstRunStatus(deps, statuses) {
|
|
33
24
|
const detected = statuses
|
|
34
25
|
.filter((status) => status.readiness.kind === "cli")
|
|
@@ -67,28 +58,7 @@ export async function firstRunStatus(deps, statuses) {
|
|
|
67
58
|
};
|
|
68
59
|
writeSetting(deps.db, defaultsKey, JSON.stringify(defaults));
|
|
69
60
|
}
|
|
70
|
-
|
|
71
|
-
.filter((cli) => cli.usable && cli.id !== "codex-image")
|
|
72
|
-
.map((cli) => cli.displayName);
|
|
73
|
-
const systemVoice = statuses.some((status) => status.readiness.kind === "local" && status.readiness.available);
|
|
74
|
-
const narration = systemVoice
|
|
75
|
-
? "Narration can use your computer's built-in voice (System voice in Settings → Voices); add an ElevenLabs or OpenAI key later for a better one."
|
|
76
|
-
: "Narration still needs a voice provider key, or turn Audio off or upload your own.";
|
|
77
|
-
return {
|
|
78
|
-
firstRun,
|
|
79
|
-
detected,
|
|
80
|
-
defaults,
|
|
81
|
-
message: !firstRun
|
|
82
|
-
? null
|
|
83
|
-
: names.length > 0
|
|
84
|
-
? "You can make a video now, no API keys needed."
|
|
85
|
-
: "No AI command-line tool was found on this computer.",
|
|
86
|
-
detail: !firstRun
|
|
87
|
-
? null
|
|
88
|
-
: names.length > 0
|
|
89
|
-
? `Slopify found ${list(names)} and picked ${names.length === 1 ? "it" : "them"} on Play. ${narration} Keys are optional extras in Settings → Providers.`
|
|
90
|
-
: "Install Claude Code, Codex or Antigravity CLI and sign in once in a terminal, then reload this page. Or paste an API key in Settings → Providers.",
|
|
91
|
-
};
|
|
61
|
+
return { firstRun, detected, defaults };
|
|
92
62
|
}
|
|
93
63
|
async function firstModel(deps, provider, family) {
|
|
94
64
|
try {
|
|
@@ -99,8 +69,3 @@ async function firstModel(deps, provider, family) {
|
|
|
99
69
|
return "";
|
|
100
70
|
}
|
|
101
71
|
}
|
|
102
|
-
function list(names) {
|
|
103
|
-
return names.length <= 1
|
|
104
|
-
? (names[0] ?? "")
|
|
105
|
-
: `${names.slice(0, -1).join(", ")} and ${names[names.length - 1]}`;
|
|
106
|
-
}
|
|
@@ -1,10 +1,15 @@
|
|
|
1
1
|
import { choiceSites, retiredModelUsage, slotLabels, } from "../model-upkeep/usage.js";
|
|
2
2
|
import { cliPathStatus } from "./cli-paths.js";
|
|
3
|
-
import { keyGuides } from "./key-guides.js";
|
|
4
3
|
import { testProviderKey } from "./key-test.js";
|
|
5
4
|
import { isLocalCliProvider } from "./model.js";
|
|
6
5
|
import { modelReach } from "./model-reach.js";
|
|
7
6
|
import { providerStatuses } from "./readiness.js";
|
|
7
|
+
// The kinds as Keys & services heads its lists.
|
|
8
|
+
const familyTitles = {
|
|
9
|
+
llm: "Writing services",
|
|
10
|
+
tts: "Voice services",
|
|
11
|
+
image: "Image services",
|
|
12
|
+
};
|
|
8
13
|
const signInCommand = {
|
|
9
14
|
"claude-code": "claude auth login",
|
|
10
15
|
codex: "codex login",
|
|
@@ -13,7 +18,16 @@ const signInCommand = {
|
|
|
13
18
|
function loginSource(id) {
|
|
14
19
|
return id === "codex-image" ? "codex" : id;
|
|
15
20
|
}
|
|
16
|
-
// Where
|
|
21
|
+
// Where a model is chosen, as a person finds it: "template “Weekly”", "the draft “Moon” on
|
|
22
|
+
// Create".
|
|
23
|
+
function siteWords(kind, name) {
|
|
24
|
+
if (kind !== "draft")
|
|
25
|
+
return `${kind} “${name}”`;
|
|
26
|
+
return name === "Untitled draft"
|
|
27
|
+
? "an untitled draft on Create"
|
|
28
|
+
: `the draft “${name}” on Create`;
|
|
29
|
+
}
|
|
30
|
+
// Where each provider's models are chosen, as "Writing model in template “Weekly”".
|
|
17
31
|
function uses(db) {
|
|
18
32
|
const found = new Map();
|
|
19
33
|
for (const site of choiceSites(db))
|
|
@@ -24,7 +38,7 @@ function uses(db) {
|
|
|
24
38
|
const list = found.get(choice.provider) ?? [];
|
|
25
39
|
list.push({
|
|
26
40
|
model: choice.model,
|
|
27
|
-
where: `${slotLabels[slot]} in ${site.kind
|
|
41
|
+
where: `${slotLabels[slot]} in ${siteWords(site.kind, site.name)}`,
|
|
28
42
|
});
|
|
29
43
|
found.set(choice.provider, list);
|
|
30
44
|
}
|
|
@@ -37,7 +51,7 @@ function overall(checks, used, setUp) {
|
|
|
37
51
|
return "warning";
|
|
38
52
|
return setUp || used ? "ok" : "unused";
|
|
39
53
|
}
|
|
40
|
-
// "Check all" on Settings →
|
|
54
|
+
// "Check all" on Settings → Keys & services: each command-line tool found and signed in, each saved
|
|
41
55
|
// key accepted by its provider, and each chosen model still offered and answering for the key.
|
|
42
56
|
// `only` checks one provider: its row's Check again, or a sign-in fix-it's.
|
|
43
57
|
export async function checkProviderHealth(deps, only) {
|
|
@@ -85,7 +99,7 @@ async function cliHealth(deps, status, used) {
|
|
|
85
99
|
label: "Installed",
|
|
86
100
|
state: used.length > 0 ? "problem" : "skipped",
|
|
87
101
|
detail: readiness.issue ??
|
|
88
|
-
`${name} was not found on this computer. Install it, sign in once in a terminal, then
|
|
102
|
+
`${name} was not found on this computer. Install it, sign in once in a terminal, then press Check again. If it is installed somewhere unusual, set its path in Settings → Keys & services.`,
|
|
89
103
|
});
|
|
90
104
|
return { ...base, state: used.length > 0 ? "problem" : "unused", checks };
|
|
91
105
|
}
|
|
@@ -93,7 +107,7 @@ async function cliHealth(deps, status, used) {
|
|
|
93
107
|
label: "Installed",
|
|
94
108
|
state: readiness.issueKind === "version" ? "problem" : "ok",
|
|
95
109
|
detail: readiness.issueKind === "version"
|
|
96
|
-
? (readiness.issue ?? `Update ${name}, then
|
|
110
|
+
? (readiness.issue ?? `Update ${name}, then press Check again.`)
|
|
97
111
|
: `Found ${command}${readiness.version === undefined ? "" : ` (version ${readiness.version})`}.`,
|
|
98
112
|
});
|
|
99
113
|
const source = loginSource(status.id);
|
|
@@ -115,7 +129,7 @@ async function cliHealth(deps, status, used) {
|
|
|
115
129
|
? {
|
|
116
130
|
label: "Signed in",
|
|
117
131
|
state: "problem",
|
|
118
|
-
detail: `${name} is not signed in. Open a terminal on the computer running it, run "${signInCommand[source]}" and sign in, then
|
|
132
|
+
detail: `${name} is not signed in. Open a terminal on the computer running it, run "${signInCommand[source]}" and sign in, then press Check again.`,
|
|
119
133
|
}
|
|
120
134
|
: {
|
|
121
135
|
label: "Signed in",
|
|
@@ -141,7 +155,7 @@ async function cliModels(deps, status, used) {
|
|
|
141
155
|
return {
|
|
142
156
|
label: "Chosen models",
|
|
143
157
|
state: "warning",
|
|
144
|
-
detail: `Slopify could not read ${status.displayName}'s model list, so it could not check the chosen models. Make sure it is signed in and up to date, then
|
|
158
|
+
detail: `Slopify could not read ${status.displayName}'s model list, so it could not check the chosen models. Make sure it is signed in and up to date, then press Check again.`,
|
|
145
159
|
};
|
|
146
160
|
}
|
|
147
161
|
const ids = new Set(offered.map((model) => model.id));
|
|
@@ -174,14 +188,14 @@ async function reachable(deps, status, used) {
|
|
|
174
188
|
return {
|
|
175
189
|
label: "Model reachable",
|
|
176
190
|
state: "problem",
|
|
177
|
-
detail: `${name} does not offer ${describe(missing)} to this key: the model id is wrong, retired, or the account has no access to it. Pick another model there and save, or enable the model for your account, then
|
|
191
|
+
detail: `${name} does not offer ${describe(missing)} to this key: the model id is wrong, retired, or the account has no access to it. Pick another model there and save, or enable the model for your account, then press Check again.`,
|
|
178
192
|
};
|
|
179
193
|
const unknown = [...found].filter(([, reach]) => reach === "unknown").map(([model]) => model);
|
|
180
194
|
if (unknown.length > 0)
|
|
181
195
|
return {
|
|
182
196
|
label: "Model reachable",
|
|
183
197
|
state: "warning",
|
|
184
|
-
detail: `${name} didn't answer for ${unknown.join(", ")}, so Slopify couldn't confirm the key can use ${unknown.length === 1 ? "it" : "them"}. Check your internet connection, then
|
|
198
|
+
detail: `${name} didn't answer for ${unknown.join(", ")}, so Slopify couldn't confirm the key can use ${unknown.length === 1 ? "it" : "them"}. Check your internet connection, then press Check again.`,
|
|
185
199
|
};
|
|
186
200
|
return {
|
|
187
201
|
label: "Model reachable",
|
|
@@ -198,16 +212,17 @@ function describe(missing) {
|
|
|
198
212
|
async function keyedHealth(deps, status, used, retired) {
|
|
199
213
|
const { readiness } = status;
|
|
200
214
|
const base = { id: status.id, displayName: status.displayName, family: status.family };
|
|
201
|
-
const guide = keyGuides[status.id];
|
|
202
215
|
const checks = [];
|
|
203
216
|
const hasKey = readiness.kind === "keyed" && readiness.hasKey;
|
|
204
217
|
if (!hasKey) {
|
|
218
|
+
// Said where it is read, Keys & services: the service's own setup, under its kind, has
|
|
219
|
+
// the key box and where to get a key.
|
|
205
220
|
checks.push({
|
|
206
|
-
label: "Key
|
|
221
|
+
label: "Key",
|
|
207
222
|
state: used.length > 0 ? "problem" : "skipped",
|
|
208
|
-
detail:
|
|
223
|
+
detail: `${used.length > 0 ? `None is saved, and ${status.displayName} is chosen for ${describe(used)}.` : "None is saved."} Open ${status.displayName} under ${familyTitles[status.family]}, paste a key and press Save.`,
|
|
209
224
|
});
|
|
210
|
-
return { ...base, state: used.length > 0 ? "
|
|
225
|
+
return { ...base, state: used.length > 0 ? "no-key" : "unused", checks };
|
|
211
226
|
}
|
|
212
227
|
const test = await testProviderKey(deps, status.id);
|
|
213
228
|
checks.push({
|
|
@@ -228,7 +243,7 @@ async function keyedHealth(deps, status, used, retired) {
|
|
|
228
243
|
state: "problem",
|
|
229
244
|
detail: `${describe(mine.map((usage) => ({
|
|
230
245
|
model: usage.model,
|
|
231
|
-
where: `${slotLabels[usage.slot]} in ${usage.kind
|
|
246
|
+
where: `${slotLabels[usage.slot]} in ${siteWords(usage.kind, usage.name)}`,
|
|
232
247
|
})))} ${mine.length === 1 ? "is" : "are"} no longer offered. Use Switch in Settings → Models → Retired models in use.`,
|
|
233
248
|
});
|
|
234
249
|
else if (used.length > 0)
|
|
@@ -21,7 +21,7 @@ export function keyTestOutcome(provider, answer, checkedAt, badKey) {
|
|
|
21
21
|
const guide = keyGuides[provider];
|
|
22
22
|
const keyPage = guide === undefined ? "the provider's website" : guide.keyPage.url;
|
|
23
23
|
const billing = guide?.billing?.url;
|
|
24
|
-
const place = `Settings →
|
|
24
|
+
const place = `Settings → Keys & services → ${name}`;
|
|
25
25
|
const done = (result, message) => ({
|
|
26
26
|
provider,
|
|
27
27
|
result,
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { probeRead } from "./probe-read.js";
|
|
1
2
|
import { keyOf } from "./repo.js";
|
|
2
3
|
// ceiling: a provider chosen in many templates still costs a handful of reads, not one per
|
|
3
4
|
// model anywhere in the library.
|
|
@@ -12,23 +13,8 @@ export async function modelReach(deps, provider, models) {
|
|
|
12
13
|
const known = reads.get(url);
|
|
13
14
|
if (known !== undefined)
|
|
14
15
|
return known;
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
const response = await deps.fetch(url, {
|
|
18
|
-
headers: probe.headers(key),
|
|
19
|
-
signal: AbortSignal.timeout(15_000),
|
|
20
|
-
redirect: "error",
|
|
21
|
-
});
|
|
22
|
-
// A list is read whole; a single model's page only for its status.
|
|
23
|
-
const body = probe.model?.lists === undefined ? "" : await response.text();
|
|
24
|
-
if (probe.model?.lists === undefined)
|
|
25
|
-
await response.body?.cancel().catch(() => { });
|
|
26
|
-
return { status: response.status, body };
|
|
27
|
-
}
|
|
28
|
-
catch {
|
|
29
|
-
return undefined;
|
|
30
|
-
}
|
|
31
|
-
})();
|
|
16
|
+
// A list is read whole; a single model's page only for its status.
|
|
17
|
+
const started = probeRead(deps.fetch, url, probe.headers(key), probe.model?.lists !== undefined);
|
|
32
18
|
reads.set(url, started);
|
|
33
19
|
return started;
|
|
34
20
|
};
|
|
@@ -11,8 +11,8 @@ export const providerIds = [
|
|
|
11
11
|
"claude-code",
|
|
12
12
|
"codex",
|
|
13
13
|
"gemini",
|
|
14
|
-
"elevenlabs",
|
|
15
14
|
"openai-tts",
|
|
15
|
+
"elevenlabs",
|
|
16
16
|
"cartesia",
|
|
17
17
|
"inworld",
|
|
18
18
|
"system-voice",
|
|
@@ -37,6 +37,13 @@ export const systemVoiceProvider = "system-voice";
|
|
|
37
37
|
export function isUncataloguedProvider(id) {
|
|
38
38
|
return isLocalCliProvider(id) || id === systemVoiceProvider;
|
|
39
39
|
}
|
|
40
|
+
// The local stand-ins the bundled samples were made with (`src/sample-build/`): no catalogue
|
|
41
|
+
// lists them, they cost nothing, and they exist only while the sample archives are built, so
|
|
42
|
+
// a sample project names them but nothing new can run on them.
|
|
43
|
+
export const sampleStandInProviders = ["sample-writer", "sample-artist", "sample-voice"];
|
|
44
|
+
export function isSampleStandIn(id) {
|
|
45
|
+
return sampleStandInProviders.includes(id);
|
|
46
|
+
}
|
|
40
47
|
export const providers = [
|
|
41
48
|
{ id: "openrouter", family: "llm", displayName: "OpenRouter", auth: "key" },
|
|
42
49
|
{
|
|
@@ -63,8 +70,10 @@ export const providers = [
|
|
|
63
70
|
binary: "agy",
|
|
64
71
|
versionArgs: ["--version"],
|
|
65
72
|
},
|
|
66
|
-
|
|
73
|
+
// OpenAI first among the voices: the voices Slopify sets up are OpenAI's, so its key narrates
|
|
74
|
+
// at once, while ElevenLabs, Cartesia and Inworld also need a voice saved under Voices.
|
|
67
75
|
{ id: "openai-tts", family: "tts", displayName: "OpenAI", auth: "key" },
|
|
76
|
+
{ id: "elevenlabs", family: "tts", displayName: "ElevenLabs", auth: "key" },
|
|
68
77
|
{ id: "cartesia", family: "tts", displayName: "Cartesia", auth: "key" },
|
|
69
78
|
{ id: "inworld", family: "tts", displayName: "Inworld", auth: "key" },
|
|
70
79
|
{ id: "system-voice", family: "tts", displayName: "System voice", auth: "local" },
|
|
@@ -137,4 +146,7 @@ export const defaultSettings = {
|
|
|
137
146
|
appearance: "system",
|
|
138
147
|
loudness: defaultLoudness,
|
|
139
148
|
subtitleModel: defaultEnglishSubtitleModel,
|
|
149
|
+
videoEncoder: "cpu",
|
|
150
|
+
clipEncodeSpeed: "thorough",
|
|
151
|
+
renderPriority: "background",
|
|
140
152
|
};
|
|
@@ -2,17 +2,24 @@ import { z } from "zod";
|
|
|
2
2
|
import { englishSubtitleModels } from "../../kernel/ports/subtitles.js";
|
|
3
3
|
import { silenceGapSecondsMax } from "../admission/rules.js";
|
|
4
4
|
import { loudnessFields } from "../loudness/model.js";
|
|
5
|
+
import { clipEncodeSpeeds, renderPriorities, videoEncoders } from "../video/encoding.js";
|
|
5
6
|
import { appearances, defaultSettings } from "./model.js";
|
|
6
|
-
import {
|
|
7
|
+
import { readJsonSetting, writeSetting } from "./repo.js";
|
|
7
8
|
// The `settings` table is key/value, so each setting is its own row and a value the app
|
|
8
9
|
// does not understand costs only that one setting.
|
|
9
10
|
const silenceGapKey = "silenceGapSeconds";
|
|
10
11
|
const appearanceKey = "appearance";
|
|
11
12
|
const loudnessKey = "loudness";
|
|
12
13
|
const subtitleModelKey = "subtitleModel";
|
|
14
|
+
const videoEncoderKey = "videoEncoder";
|
|
15
|
+
const clipEncodeSpeedKey = "clipEncodeSpeed";
|
|
16
|
+
const renderPriorityKey = "renderPriority";
|
|
13
17
|
const silenceGapValue = z.number().int().min(0).max(silenceGapSecondsMax);
|
|
14
18
|
const appearanceValue = z.enum(appearances);
|
|
15
19
|
const subtitleModelValue = z.enum(englishSubtitleModels);
|
|
20
|
+
const videoEncoderValue = z.enum(videoEncoders);
|
|
21
|
+
const clipEncodeSpeedValue = z.enum(clipEncodeSpeeds);
|
|
22
|
+
const renderPriorityValue = z.enum(renderPriorities);
|
|
16
23
|
const loudnessValue = z.object({
|
|
17
24
|
enabled: z.boolean(),
|
|
18
25
|
videoLufs: z.number(),
|
|
@@ -24,6 +31,9 @@ export function readSettings(deps) {
|
|
|
24
31
|
appearance: parsed(deps, appearanceKey, appearanceValue, defaultSettings.appearance),
|
|
25
32
|
loudness: parsed(deps, loudnessKey, loudnessValue, defaultSettings.loudness),
|
|
26
33
|
subtitleModel: parsed(deps, subtitleModelKey, subtitleModelValue, defaultSettings.subtitleModel),
|
|
34
|
+
videoEncoder: parsed(deps, videoEncoderKey, videoEncoderValue, defaultSettings.videoEncoder),
|
|
35
|
+
clipEncodeSpeed: parsed(deps, clipEncodeSpeedKey, clipEncodeSpeedValue, defaultSettings.clipEncodeSpeed),
|
|
36
|
+
renderPriority: parsed(deps, renderPriorityKey, renderPriorityValue, defaultSettings.renderPriority),
|
|
27
37
|
};
|
|
28
38
|
}
|
|
29
39
|
export function saveSettings(deps, settings) {
|
|
@@ -46,7 +56,25 @@ export function saveSettings(deps, settings) {
|
|
|
46
56
|
if (!subtitleModelValue.safeParse(settings.subtitleModel).success) {
|
|
47
57
|
fields.push({
|
|
48
58
|
field: "subtitleModel",
|
|
49
|
-
message: "Choose Most accurate, Balanced or Smallest in Settings → Production defaults →
|
|
59
|
+
message: "Choose Most accurate, Balanced or Smallest in Settings → Production defaults → Caption timing model.",
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
if (!videoEncoderValue.safeParse(settings.videoEncoder).success) {
|
|
63
|
+
fields.push({
|
|
64
|
+
field: "videoEncoder",
|
|
65
|
+
message: "Choose Processor (x264) or NVIDIA graphics card (NVENC) in Settings → Production defaults → Video encoder.",
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
if (!clipEncodeSpeedValue.safeParse(settings.clipEncodeSpeed).success) {
|
|
69
|
+
fields.push({
|
|
70
|
+
field: "clipEncodeSpeed",
|
|
71
|
+
message: "Choose Thorough or Fast in Settings → Production defaults → Clip encoding.",
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
if (!renderPriorityValue.safeParse(settings.renderPriority).success) {
|
|
75
|
+
fields.push({
|
|
76
|
+
field: "renderPriority",
|
|
77
|
+
message: "Choose Normal or Background in Settings → Production defaults → Encoding priority.",
|
|
50
78
|
});
|
|
51
79
|
}
|
|
52
80
|
if (fields.length > 0) {
|
|
@@ -56,31 +84,14 @@ export function saveSettings(deps, settings) {
|
|
|
56
84
|
writeSetting(deps.db, appearanceKey, JSON.stringify(settings.appearance));
|
|
57
85
|
writeSetting(deps.db, loudnessKey, JSON.stringify(settings.loudness));
|
|
58
86
|
writeSetting(deps.db, subtitleModelKey, JSON.stringify(settings.subtitleModel));
|
|
87
|
+
writeSetting(deps.db, videoEncoderKey, JSON.stringify(settings.videoEncoder));
|
|
88
|
+
writeSetting(deps.db, clipEncodeSpeedKey, JSON.stringify(settings.clipEncodeSpeed));
|
|
89
|
+
writeSetting(deps.db, renderPriorityKey, JSON.stringify(settings.renderPriority));
|
|
59
90
|
return { ok: true, settings };
|
|
60
91
|
}
|
|
61
92
|
// A stored value is JSON, and a hand-edited database is the only way it can be anything
|
|
62
93
|
// but what was saved. Such a row falls back to the default and says so in the log rather
|
|
63
94
|
// than reaching the app or taking the settings page down.
|
|
64
95
|
function parsed(deps, key, schema, fallback) {
|
|
65
|
-
|
|
66
|
-
if (stored === undefined) {
|
|
67
|
-
return fallback;
|
|
68
|
-
}
|
|
69
|
-
const result = schema.safeParse(jsonOf(stored));
|
|
70
|
-
if (result.success) {
|
|
71
|
-
return result.data;
|
|
72
|
-
}
|
|
73
|
-
deps.log.write("warn", "settings.invalid", {
|
|
74
|
-
detail: `setting ${key} is not a value this build understands; using the default`,
|
|
75
|
-
});
|
|
76
|
-
return fallback;
|
|
77
|
-
}
|
|
78
|
-
function jsonOf(stored) {
|
|
79
|
-
try {
|
|
80
|
-
return JSON.parse(stored);
|
|
81
|
-
}
|
|
82
|
-
catch {
|
|
83
|
-
// Not JSON at all: the same answer as JSON of the wrong shape, decided by the caller.
|
|
84
|
-
return undefined;
|
|
85
|
-
}
|
|
96
|
+
return readJsonSetting(deps.db, key, schema, fallback, deps.log);
|
|
86
97
|
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export async function probeRead(fetch, url, headers, withBody) {
|
|
2
|
+
try {
|
|
3
|
+
const response = await fetch(url, {
|
|
4
|
+
headers,
|
|
5
|
+
signal: AbortSignal.timeout(15_000),
|
|
6
|
+
redirect: "error",
|
|
7
|
+
});
|
|
8
|
+
const body = withBody ? await response.text() : "";
|
|
9
|
+
if (!withBody)
|
|
10
|
+
await response.body?.cancel().catch(() => { });
|
|
11
|
+
return { status: response.status, body };
|
|
12
|
+
}
|
|
13
|
+
catch {
|
|
14
|
+
return undefined;
|
|
15
|
+
}
|
|
16
|
+
}
|