@gentbajko/slopify 4.0.0 → 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 +1 -1
- package/dist/adapters/alignment/audio.js +1 -1
- package/dist/adapters/alignment/cache.js +1 -1
- package/dist/adapters/alignment/multilingual.js +2 -1
- package/dist/adapters/alignment/runner.js +1 -1
- package/dist/adapters/alignment/worker.js +1 -1
- package/dist/adapters/explain.js +10 -9
- package/dist/adapters/host-cli/index.js +2 -1
- package/dist/adapters/host-cli/transport.js +2 -1
- package/dist/adapters/image/bytes.js +2 -1
- package/dist/adapters/image/codex.js +6 -5
- package/dist/adapters/image/fal.js +4 -3
- package/dist/adapters/image/google.js +3 -2
- package/dist/adapters/image/models.js +2 -2
- package/dist/adapters/image/openai.js +2 -1
- package/dist/adapters/image/replicate.js +3 -2
- package/dist/adapters/image/video.js +2 -1
- package/dist/adapters/key-probes.js +1 -1
- package/dist/adapters/llm/antigravity-models.js +1 -1
- package/dist/adapters/llm/antigravity.js +7 -6
- package/dist/adapters/llm/assistant-cli.js +1 -1
- package/dist/adapters/llm/claude-code-models.js +1 -1
- package/dist/adapters/llm/claude-code.js +3 -2
- package/dist/adapters/llm/codex-models.js +1 -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 +2 -1
- package/dist/adapters/llm/run-cli.js +3 -2
- package/dist/adapters/tts/gemini.js +2 -1
- package/dist/adapters/tts/inworld-async.js +2 -1
- package/dist/adapters/tts/inworld.js +3 -2
- package/dist/adapters/tts/system.js +2 -1
- package/dist/catalog/registry.js +20 -13
- package/dist/catalog/schema.js +2 -1
- package/dist/catalog/validate.js +5 -2
- package/dist/edge/http/actions.js +7 -7
- package/dist/edge/http/app.js +3 -3
- package/dist/edge/http/assistant.js +3 -1
- package/dist/edge/http/backups.js +1 -1
- package/dist/edge/http/channel-memory.js +2 -2
- package/dist/edge/http/channels.js +1 -1
- package/dist/edge/http/checkpoints.js +7 -5
- package/dist/edge/http/design-templates.js +3 -3
- package/dist/edge/http/designs.js +1 -1
- package/dist/edge/http/document-themes.js +3 -3
- package/dist/edge/http/draft-problem.js +1 -1
- package/dist/edge/http/extension-auth.js +1 -1
- package/dist/edge/http/home.js +9 -1
- package/dist/edge/http/model-checks.js +4 -4
- package/dist/edge/http/onboarding.js +29 -12
- package/dist/edge/http/outputs.js +3 -2
- package/dist/edge/http/patch-notes.js +2 -2
- package/dist/edge/http/planning.js +1 -1
- package/dist/edge/http/project-create.js +1 -1
- package/dist/edge/http/project-templates.js +2 -2
- package/dist/edge/http/projects.js +22 -4
- package/dist/edge/http/prompts.js +2 -2
- package/dist/edge/http/providers.js +9 -10
- package/dist/edge/http/refusal.js +1 -1
- package/dist/edge/http/remote-access.js +4 -4
- package/dist/edge/http/remote-guard.js +2 -2
- package/dist/edge/http/reviews.js +1 -1
- package/dist/edge/http/revision-files.js +1 -1
- package/dist/edge/http/revisions.js +4 -4
- package/dist/edge/http/schedules.js +13 -11
- package/dist/edge/http/settings.js +6 -2
- package/dist/edge/http/social.js +5 -4
- package/dist/edge/http/staging.js +1 -1
- package/dist/edge/http/storage.js +12 -4
- package/dist/edge/http/studio.js +71 -19
- package/dist/edge/http/style-preview.js +2 -2
- package/dist/edge/http/template-studio.js +12 -9
- package/dist/edge/http/trash.js +1 -1
- package/dist/edge/http/youtube-edits.js +1 -1
- package/dist/edge/mcp/excluded-routes.js +4 -3
- package/dist/edge/mcp/instructions.js +3 -3
- package/dist/edge/mcp/library-tools.js +8 -5
- package/dist/edge/mcp/remake-tools.js +7 -4
- package/dist/edge/mcp/server.js +1 -1
- package/dist/edge/mcp/tools-channels.js +9 -9
- package/dist/edge/mcp/tools-designs.js +9 -9
- package/dist/edge/mcp/tools-library.js +7 -7
- package/dist/edge/mcp/tools-planning.js +18 -18
- package/dist/edge/mcp/tools-projects.js +10 -10
- package/dist/edge/mcp/tools-publishing.js +9 -9
- package/dist/edge/mcp/tools-runs.js +9 -9
- package/dist/edge/mcp/tools-schedules.js +6 -6
- package/dist/edge/mcp/tools-setup.js +20 -14
- package/dist/edge/mcp/tools-social.js +5 -4
- package/dist/edge/mcp/tools-storage.js +3 -3
- package/dist/edge/mcp/tools-template-studio.js +18 -18
- package/dist/edge/remote-access/model.js +1 -1
- package/dist/extension/slopify-studio-chrome.zip +0 -0
- package/dist/extension/slopify-studio-firefox.zip +0 -0
- package/dist/kernel/db/migrations/0078-uploaded-from-links.sql +7 -0
- package/dist/kernel/pipeline.js +12 -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 +11 -3
- package/dist/kernel/runner/providers.js +3 -2
- package/dist/kernel/runner/standalone.js +2 -2
- package/dist/main.js +5 -3
- package/dist/model-catalog.js +20 -0
- package/dist/patch-notes/4.0.0.md +13 -13
- package/dist/patch-notes/4.1.0.md +42 -0
- package/dist/patch-notes/index.json +6 -0
- package/dist/slices/admission/listing-facts.js +95 -0
- package/dist/slices/admission/repo.js +9 -6
- package/dist/slices/admission/rules.js +7 -7
- package/dist/slices/admission/state-words.js +34 -5
- package/dist/slices/assistant/cost.js +1 -1
- package/dist/slices/assistant/model.js +10 -3
- package/dist/slices/assistant/turn.js +5 -3
- package/dist/slices/autopilot/warnings.js +1 -1
- package/dist/slices/backups/service.js +5 -5
- package/dist/slices/channels/cast-images.js +1 -1
- package/dist/slices/channels/schema.js +1 -1
- package/dist/slices/channels/videos.js +1 -1
- package/dist/slices/control/providers.js +2 -2
- package/dist/slices/designs/budget.js +1 -1
- package/dist/slices/designs/calls.js +2 -2
- package/dist/slices/designs/check-context.js +4 -4
- package/dist/slices/designs/fields.js +4 -4
- package/dist/slices/designs/gate.js +7 -5
- package/dist/slices/designs/render-input.js +1 -1
- package/dist/slices/designs/requests.js +1 -1
- package/dist/slices/designs/runner.js +1 -1
- package/dist/slices/designs/sample.js +2 -1
- package/dist/slices/designs/service.js +1 -1
- package/dist/slices/designs/template-checks.js +2 -2
- package/dist/slices/designs/template-make.js +3 -3
- package/dist/slices/designs/templates.js +3 -3
- package/dist/slices/document/fonts.js +1 -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 +2 -2
- package/dist/slices/estimate/index.js +28 -22
- package/dist/slices/estimate/requests.js +4 -4
- package/dist/slices/fixes/rules.js +3 -1
- package/dist/slices/fonts/catalog.js +2 -1
- package/dist/slices/fonts/coverage.js +2 -1
- package/dist/slices/images/appearance.js +2 -1
- package/dist/slices/images/scenes.js +2 -1
- package/dist/slices/keywords/digest.js +2 -1
- package/dist/slices/keywords/rules.js +2 -2
- package/dist/slices/keywords/specs.js +5 -5
- package/dist/slices/keywords/values.js +1 -1
- package/dist/slices/library/transfer.js +3 -3
- package/dist/slices/loudness/loudnorm.js +2 -1
- package/dist/slices/loudness/model.js +3 -2
- package/dist/slices/loudness/sentence-level.js +2 -1
- package/dist/slices/model-checks/rules.js +13 -0
- package/dist/slices/model-upkeep/model.js +1 -1
- package/dist/slices/model-upkeep/switch.js +3 -3
- package/dist/slices/model-upkeep/usage.js +13 -2
- package/dist/slices/narration/breaths.js +2 -1
- package/dist/slices/narration/clean-pieces.js +2 -1
- package/dist/slices/narration/pauses-model.js +3 -2
- package/dist/slices/narration/pronunciation.js +3 -2
- package/dist/slices/notifications/subject.js +2 -2
- package/dist/slices/onboarding/copy.js +19 -2
- package/dist/slices/onboarding/first-run.js +13 -38
- package/dist/slices/onboarding/install.js +2 -1
- package/dist/slices/onboarding/model.js +26 -21
- package/dist/slices/onboarding/preferred-model.js +14 -0
- package/dist/slices/onboarding/quick-short.js +103 -64
- package/dist/slices/onboarding/sample.js +4 -1
- package/dist/slices/onboarding/state.js +7 -0
- package/dist/slices/onboarding/template.js +6 -5
- package/dist/slices/outputs/add.js +3 -2
- package/dist/slices/outputs/service.js +2 -2
- package/dist/slices/patch-notes/library.js +1 -1
- package/dist/slices/play-drafts/readiness.js +3 -3
- package/dist/slices/play-drafts/review-inputs.js +1 -1
- package/dist/slices/play-drafts/service.js +17 -7
- 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/setup-sections.js +26 -0
- package/dist/slices/project-templates/setup.js +6 -1
- package/dist/slices/rebuild/preview-details.js +5 -5
- package/dist/slices/rebuild/recipe-preparation.js +1 -1
- package/dist/slices/rebuild/recipe-save.js +2 -2
- package/dist/slices/rebuild/recipe-validation.js +5 -4
- package/dist/slices/rebuild/recipe-voices.js +3 -2
- package/dist/slices/rebuild/recipe-work.js +4 -4
- package/dist/slices/rebuild/recovery.js +6 -6
- package/dist/slices/rebuild/remake-outdated.js +5 -5
- package/dist/slices/rebuild/review-redo.js +1 -1
- package/dist/slices/rebuild/runtime-animate.js +2 -2
- package/dist/slices/rebuild/runtime-article.js +4 -3
- package/dist/slices/rebuild/runtime-document.js +5 -4
- package/dist/slices/rebuild/runtime-export-bed.js +2 -1
- package/dist/slices/rebuild/runtime-export-edit.js +2 -1
- package/dist/slices/rebuild/runtime-export-inputs.js +5 -4
- package/dist/slices/rebuild/runtime-export-short.js +1 -1
- package/dist/slices/rebuild/runtime-export.js +4 -3
- package/dist/slices/rebuild/runtime-image.js +4 -3
- package/dist/slices/rebuild/runtime-local.js +3 -2
- package/dist/slices/rebuild/runtime-narration-text.js +3 -2
- package/dist/slices/rebuild/runtime-provider.js +7 -6
- package/dist/slices/rebuild/runtime-review.js +6 -5
- package/dist/slices/rebuild/runtime-shorts.js +6 -5
- package/dist/slices/rebuild/runtime-store.js +17 -2
- package/dist/slices/rebuild/runtime-subtitles.js +13 -12
- package/dist/slices/rebuild/runtime-voices.js +2 -1
- package/dist/slices/rebuild/runtime-youtube.js +3 -2
- package/dist/slices/rebuild/service-readiness.js +11 -3
- package/dist/slices/research/synthesis.js +3 -2
- package/dist/slices/reviews/external-model.js +1 -1
- package/dist/slices/reviews/holds.js +5 -4
- package/dist/slices/reviews/model.js +8 -5
- package/dist/slices/reviews/rules.js +3 -3
- package/dist/slices/reviews/verdict.js +2 -1
- package/dist/slices/revisions/image-plan.js +1 -1
- package/dist/slices/revisions/mutation-assets.js +2 -1
- package/dist/slices/revisions/mutations.js +3 -2
- package/dist/slices/schedules/agenda.js +7 -5
- package/dist/slices/schedules/design-check.js +4 -4
- package/dist/slices/schedules/design-run.js +3 -3
- package/dist/slices/schedules/repo.js +6 -5
- package/dist/slices/schedules/schema.js +3 -0
- package/dist/slices/schedules/topics.js +5 -5
- package/dist/slices/settings/first-run.js +6 -32
- package/dist/slices/settings/health.js +30 -15
- package/dist/slices/settings/key-test.js +1 -1
- package/dist/slices/settings/model.js +4 -2
- package/dist/slices/settings/playback.js +2 -2
- package/dist/slices/shorts/keep.js +2 -1
- package/dist/slices/shorts/pick.js +4 -3
- package/dist/slices/shorts/prompts.js +2 -1
- package/dist/slices/shorts/render.js +3 -2
- package/dist/slices/social/media.js +5 -4
- package/dist/slices/social/pack.js +1 -1
- package/dist/slices/social/post-prompt.js +2 -1
- package/dist/slices/social/reshape.js +2 -2
- package/dist/slices/social/serve.js +1 -1
- package/dist/slices/social/service.js +3 -2
- package/dist/slices/social/state.js +1 -1
- package/dist/slices/storage/backup-import.js +2 -2
- package/dist/slices/storage/portable.js +1 -1
- package/dist/slices/storage/trim.js +23 -13
- package/dist/slices/studio/calendar.js +3 -1
- 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/plan.js +1 -1
- package/dist/slices/studio/settings.js +6 -6
- package/dist/slices/studio/videos.js +23 -4
- package/dist/slices/style-preview/render.js +3 -3
- package/dist/slices/style-preview/schema.js +1 -1
- package/dist/slices/subtitles/captions.js +1 -1
- package/dist/slices/template-studio/anatomy-view.js +46 -3
- package/dist/slices/template-studio/bakeoff.js +3 -3
- package/dist/slices/template-studio/budget.js +6 -3
- package/dist/slices/template-studio/cards.js +78 -0
- package/dist/slices/template-studio/checks.js +99 -50
- package/dist/slices/template-studio/for-all.js +5 -5
- package/dist/slices/template-studio/free-video.js +1 -1
- package/dist/slices/template-studio/free.js +5 -5
- package/dist/slices/template-studio/model.js +19 -15
- package/dist/slices/template-studio/pack-import.js +14 -14
- package/dist/slices/template-studio/pack.js +9 -7
- package/dist/slices/template-studio/paid.js +12 -5
- package/dist/slices/template-studio/quote.js +18 -11
- package/dist/slices/template-studio/ready.js +7 -7
- package/dist/slices/template-studio/runner.js +1 -1
- package/dist/slices/template-studio/save.js +12 -12
- package/dist/slices/template-studio/say-it-as.js +3 -3
- package/dist/slices/template-studio/schedule-gate.js +1 -1
- package/dist/slices/template-studio/schedule-price.js +3 -1
- package/dist/slices/template-studio/schedule-sample.js +5 -1
- package/dist/slices/template-studio/schema.js +25 -0
- package/dist/slices/template-studio/service.js +3 -1
- package/dist/slices/template-studio/status.js +26 -9
- package/dist/slices/template-studio/topic.js +3 -3
- package/dist/slices/thumbnail/by-llm.js +1 -1
- package/dist/slices/uploads/repo.js +37 -1
- package/dist/slices/uploads/rules.js +3 -2
- package/dist/slices/video/edit-list.js +2 -2
- package/dist/slices/video/edit-settings.js +1 -1
- package/dist/slices/video/encoding.js +14 -5
- package/dist/slices/video/ffmpeg.js +2 -2
- package/dist/slices/video/plan.js +2 -1
- package/dist/slices/video/slideshow.js +1 -1
- package/dist/slices/voices/attribution.js +3 -2
- package/dist/slices/voices/model.js +9 -8
- package/dist/slices/voices/script.js +2 -1
- package/dist/slices/youtube/answer.js +2 -1
- package/dist/slices/youtube/edits-repo.js +1 -1
- package/dist/tutorials/AI-CLIs.md +25 -25
- package/dist/tutorials/Agents-MCP.md +32 -32
- package/dist/tutorials/Assistant.md +5 -5
- 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 +40 -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 +101 -76
- package/dist/tutorials/Home.md +33 -33
- package/dist/tutorials/Install.md +13 -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 +21 -21
- package/dist/tutorials/Models.md +27 -27
- package/dist/tutorials/Multiple-Voices.md +41 -40
- package/dist/tutorials/Narration-Aliases-and-Glossary.md +14 -14
- package/dist/tutorials/Notifications.md +21 -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 +12 -12
- package/dist/tutorials/Project-Page.md +118 -120
- package/dist/tutorials/Prompts-and-Templates.md +84 -0
- package/dist/tutorials/Prompts.md +45 -45
- 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 +2 -2
- package/dist/tutorials/Reviews-and-Checkpoints.md +90 -93
- package/dist/tutorials/Schedules.md +27 -28
- package/dist/tutorials/Settings-Reference.md +182 -204
- 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 +45 -42
- package/dist/tutorials/Templates.md +50 -79
- 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 +15 -15
- 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 +2 -2
- package/dist/tutorials/Library-Overview.md +0 -76
- package/dist/tutorials/Play-Images.md +0 -165
- 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-CkAI_2E7.css +0 -1
- package/dist/web/assets/index-DcD0cdfo.js +0 -334
- package/dist/web/assets/pdf-TODRwF31.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
|
@@ -13,12 +13,13 @@ export const reviewStages = [
|
|
|
13
13
|
"shorts",
|
|
14
14
|
"promise",
|
|
15
15
|
];
|
|
16
|
-
// The stages in the order a run reaches them, as the Reviews rows list them
|
|
16
|
+
// The stages in the order a run reaches them, as the Reviews rows list them: the narration is
|
|
17
|
+
// made before the images (`kernel/pipeline.ts`), as the pause ticks say.
|
|
17
18
|
export const reviewStagesInRunOrder = [
|
|
18
19
|
"article",
|
|
19
20
|
"promise",
|
|
20
|
-
"images",
|
|
21
21
|
"narration",
|
|
22
|
+
"images",
|
|
22
23
|
"thumbnail",
|
|
23
24
|
"shorts",
|
|
24
25
|
];
|
|
@@ -30,9 +31,11 @@ export const reviewers = ["model", "agent"];
|
|
|
30
31
|
// Who gave a verdict: the reviewer model, an external agent, the person (through the verdict
|
|
31
32
|
// route), or nobody when the review passed at its deadline.
|
|
32
33
|
export const reviewAnswerers = ["model", "agent", "person", "nobody"];
|
|
34
|
+
// Said to a person who never heard of MCP: a model Slopify pays per check, or an AI agent they
|
|
35
|
+
// connected themselves (Settings → AI agents), which answers for free.
|
|
33
36
|
export const reviewerLabels = {
|
|
34
|
-
model: "
|
|
35
|
-
agent: "
|
|
37
|
+
model: "An AI model",
|
|
38
|
+
agent: "Your own AI agent",
|
|
36
39
|
};
|
|
37
40
|
// ceiling: the owner's numbers. Two redos is what a person would try before looking at it
|
|
38
41
|
// themselves; five is already six paid images for one slot.
|
|
@@ -123,7 +126,7 @@ export function reviewSettingsFromForm(form, makes) {
|
|
|
123
126
|
...(valid
|
|
124
127
|
? {}
|
|
125
128
|
: {
|
|
126
|
-
retriesProblem: `Enter a number of redos between ${String(reviewRetriesMin)} and ${String(reviewRetriesMax)}
|
|
129
|
+
retriesProblem: `Enter a number of redos between ${String(reviewRetriesMin)} and ${String(reviewRetriesMax)} under Pauses and checks → Checker and stages.`,
|
|
127
130
|
}),
|
|
128
131
|
};
|
|
129
132
|
}
|
|
@@ -44,14 +44,14 @@ export function reviewFields(draft) {
|
|
|
44
44
|
if (!byAgent && (reviews.provider.trim() === "" || reviews.model.trim() === ""))
|
|
45
45
|
fields.push({
|
|
46
46
|
field: "reviews.provider",
|
|
47
|
-
message: "
|
|
47
|
+
message: "AI checks need a model to check with. Choose one under Pauses and checks → Checker and stages → Checked by, or set every check to Off.",
|
|
48
48
|
});
|
|
49
49
|
const retries = reviews.retries;
|
|
50
50
|
if (retries !== undefined &&
|
|
51
51
|
(!Number.isInteger(retries) || retries < reviewRetriesMin || retries > reviewRetriesMax))
|
|
52
52
|
fields.push({
|
|
53
53
|
field: "reviews.retries",
|
|
54
|
-
message: `Enter a number of redos between ${reviewRetriesMin} and ${reviewRetriesMax}
|
|
54
|
+
message: `Enter a number of redos between ${reviewRetriesMin} and ${reviewRetriesMax} under Pauses and checks → Checker and stages.`,
|
|
55
55
|
});
|
|
56
56
|
const pictures = active.filter((stage) => visionStages.includes(stage));
|
|
57
57
|
if (!byAgent &&
|
|
@@ -60,7 +60,7 @@ export function reviewFields(draft) {
|
|
|
60
60
|
!reviewerSeesImages(reviews.provider))
|
|
61
61
|
fields.push({
|
|
62
62
|
field: "reviews.provider",
|
|
63
|
-
message: `The ${pictures.map((stage) => reviewStageLabels[stage]).join(", ")} ${pictures.length === 1 ? "
|
|
63
|
+
message: `The ${pictures.map((stage) => reviewStageLabels[stage]).join(", ")} ${pictures.length === 1 ? "check looks" : "checks look"} at images, and Slopify can't show images to this checker. Under Pauses and checks → Checker and stages → Checked by, choose Claude Code or Codex, or set ${pictures.length === 1 ? "that check" : "those checks"} to Off.`,
|
|
64
64
|
});
|
|
65
65
|
return fields;
|
|
66
66
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
+
import { editSettings } from "../../kernel/ports/settings-places.js";
|
|
2
3
|
// The whole answer the reviewer may give: one JSON object, nothing else. A failed item names
|
|
3
4
|
// at least one reason, since a reason is what the person reads beside it.
|
|
4
5
|
export const reviewReasonMax = 500;
|
|
@@ -46,7 +47,7 @@ export function reviewMessages(material) {
|
|
|
46
47
|
},
|
|
47
48
|
];
|
|
48
49
|
}
|
|
49
|
-
const retryHint =
|
|
50
|
+
const retryHint = `Slopify asks again while attempts remain; if it keeps happening, choose another reviewer model in ${editSettings.checks}, then use Try again.`;
|
|
50
51
|
// Strict: one JSON object in the shape above, optionally inside a single ```json fence (the
|
|
51
52
|
// one wrapping models add unasked). Anything else is refused with what was wrong with it.
|
|
52
53
|
export function parseVerdict(text) {
|
|
@@ -86,7 +86,7 @@ export function replanImagePrompts(input) {
|
|
|
86
86
|
if (body === undefined) {
|
|
87
87
|
fields.push({
|
|
88
88
|
field: `imagePrompts.${String(index)}.name`,
|
|
89
|
-
message: `The image prompt "${prompt.name}" is not in
|
|
89
|
+
message: `The image prompt "${prompt.name}" is not in Prompts any more. Untick it under Images, or add it back in Prompts → Image.`,
|
|
90
90
|
});
|
|
91
91
|
continue;
|
|
92
92
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { statSync } from "node:fs";
|
|
2
2
|
import { z } from "zod";
|
|
3
|
+
import { editSettings } from "../../kernel/ports/settings-places.js";
|
|
3
4
|
import { referenceKey, thumbnailCountOf } from "../admission/model.js";
|
|
4
5
|
import { usesReference } from "../admission/rules.js";
|
|
5
6
|
import { outputPath, stagingPath } from "../storage/layout.js";
|
|
@@ -59,7 +60,7 @@ export function validateUploads(deps, edit) {
|
|
|
59
60
|
if (to.kind === "shortsMusic" && edit.config.shorts?.enabled !== true)
|
|
60
61
|
fields.push({
|
|
61
62
|
field,
|
|
62
|
-
message:
|
|
63
|
+
message: `Background music is only used by the shorts. Turn Shorts on in ${editSettings.shorts}, then add the music.`,
|
|
63
64
|
});
|
|
64
65
|
const destination = JSON.stringify(to);
|
|
65
66
|
if (staged.has(upload.stagedFileId) || destinations.has(destination))
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { transact } from "../../kernel/db/tx.js";
|
|
3
|
+
import { editSettings } from "../../kernel/ports/settings-places.js";
|
|
3
4
|
import { rebrandedEdit } from "../channels/rebrand.js";
|
|
4
5
|
import { projectChannelId, resolveChannelId, setProjectChannel } from "../channels/repo.js";
|
|
5
6
|
import { carryCheckpointGates } from "../checkpoints/recovery.js";
|
|
@@ -204,14 +205,14 @@ function settingProblems(edit) {
|
|
|
204
205
|
? []
|
|
205
206
|
: ambientBedProblems(bed).map((problem) => ({
|
|
206
207
|
field: `ambientBed.${problem.field}`,
|
|
207
|
-
message: `${problem.message} Change it
|
|
208
|
+
message: `${problem.message} Change it under Ambient sound in ${editSettings.sound}.`,
|
|
208
209
|
}))),
|
|
209
210
|
...(scaleProblem === undefined
|
|
210
211
|
? []
|
|
211
212
|
: [
|
|
212
213
|
{
|
|
213
214
|
field: "imageScale",
|
|
214
|
-
message: `${scaleProblem} Change it
|
|
215
|
+
message: `${scaleProblem} Change it under More images for long videos in ${editSettings.images}.`,
|
|
215
216
|
},
|
|
216
217
|
]),
|
|
217
218
|
];
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { derive, progressOf } from "../../kernel/runner/graph.js";
|
|
3
3
|
import { liveProject, stageStandingsByProject } from "../admission/repo.js";
|
|
4
|
-
import { readsByWaitBesideFailure } from "../admission/state-words.js";
|
|
4
|
+
import { readsByWaitBesideFailure, retryingAt } from "../admission/state-words.js";
|
|
5
5
|
import { queueEntries } from "../batch/index.js";
|
|
6
6
|
import { sampleIds } from "../onboarding/model.js";
|
|
7
7
|
import { readSampleRecord } from "../onboarding/state.js";
|
|
8
8
|
import { projectHolds } from "../reviews/holds.js";
|
|
9
9
|
import { limitWaitsByProject, listingWait } from "../run-cost/panel.js";
|
|
10
10
|
import { notTemplateSample } from "../template-studio/repo.js";
|
|
11
|
-
import { setAsideProjects,
|
|
11
|
+
import { setAsideProjects, settledProjects, uploadedProjects } from "../uploads/repo.js";
|
|
12
12
|
import { settledStatus } from "../uploads/rules.js";
|
|
13
13
|
import { nextOccurrence } from "./calendar.js";
|
|
14
14
|
import { upcomingDesignRuns } from "./design-agenda.js";
|
|
@@ -151,8 +151,8 @@ function sampleProjectIds(db) {
|
|
|
151
151
|
// What a project on the calendar asks of the person, if anything. The same readings Home's
|
|
152
152
|
// Needs you uses: a failed run, a paused run, one a restart stopped (Continue the run), or one
|
|
153
153
|
// waiting for a review. A step failed beside a wait reads by that wait and is listed apart
|
|
154
|
-
// (`stepFailed`). One Kept as is asks nothing, nor does an uploaded one that reads
|
|
155
|
-
// (`
|
|
154
|
+
// (`stepFailed`). One Kept as is asks nothing, nor does an uploaded or edited one that reads
|
|
155
|
+
// Done (`settledProjects`).
|
|
156
156
|
function needsOf(state, ask) {
|
|
157
157
|
if (state === "failed")
|
|
158
158
|
return "failed";
|
|
@@ -170,7 +170,7 @@ function needsOf(state, ask) {
|
|
|
170
170
|
// the range, with what each needs from the person and whether its video is ready to upload.
|
|
171
171
|
function projectsIn(deps, from, to) {
|
|
172
172
|
const standings = stageStandingsByProject(deps.db);
|
|
173
|
-
const settled =
|
|
173
|
+
const settled = settledProjects(deps.db);
|
|
174
174
|
const holds = projectHolds(deps.db, { standings, settled });
|
|
175
175
|
const reviewing = awaitingReview(deps.db, holds);
|
|
176
176
|
const uploads = uploadedProjects(deps.db);
|
|
@@ -224,6 +224,7 @@ function projectsIn(deps, from, to) {
|
|
|
224
224
|
!uploads.has(project.id) &&
|
|
225
225
|
!samples.has(project.id);
|
|
226
226
|
const waiting = waits.get(project.id);
|
|
227
|
+
const retry = state === "running" ? retryingAt(standings.get(project.id) ?? []) : undefined;
|
|
227
228
|
return [
|
|
228
229
|
{
|
|
229
230
|
id: project.id,
|
|
@@ -242,6 +243,7 @@ function projectsIn(deps, from, to) {
|
|
|
242
243
|
...(readsByWaitBesideFailure(state, standings.get(project.id) ?? [])
|
|
243
244
|
? { stepFailed: true }
|
|
244
245
|
: {}),
|
|
246
|
+
...(retry === undefined ? {} : { retryAt: retry }),
|
|
245
247
|
...(waiting === undefined ? {} : { limitWaits: waiting.map(listingWait) }),
|
|
246
248
|
},
|
|
247
249
|
];
|
|
@@ -11,25 +11,25 @@ export function checkDesign(deps, input) {
|
|
|
11
11
|
return {
|
|
12
12
|
ok: false,
|
|
13
13
|
reason: "invalid-input",
|
|
14
|
-
message: "A
|
|
14
|
+
message: "A poster schedule makes each row's poster ahead of its date: it has no cadence, topic generation or release days. Choose Ahead of each date.",
|
|
15
15
|
};
|
|
16
16
|
const template = getTemplate(deps.db, input.templateId);
|
|
17
17
|
if (template === undefined)
|
|
18
18
|
return { ok: false, reason: "missing-template" };
|
|
19
19
|
const plan = designPlanOf(deps.db, input.templateId);
|
|
20
20
|
if (plan === undefined)
|
|
21
|
-
return refuse(`The schedule wasn't saved. ${template.name} doesn't say which
|
|
21
|
+
return refuse(`The schedule wasn't saved. ${template.name} doesn't say which keyword holds each date. Open it from Templates → Poster templates, choose the date keyword under Schedule, save it, then save the schedule again.`);
|
|
22
22
|
const fields = designFields(plan.document);
|
|
23
23
|
const named = fields.length === 0 ? "none" : fields.map((one) => `{{${one}}}`).join(", ");
|
|
24
24
|
const problems = [];
|
|
25
25
|
for (const keyword of Object.keys(input.values))
|
|
26
26
|
if (!fields.includes(keyword))
|
|
27
|
-
problems.push(`{{${keyword}}} (for every row) is not a
|
|
27
|
+
problems.push(`{{${keyword}}} (for every row) is not a keyword of this template (its keywords are ${named}). Remove it.`);
|
|
28
28
|
for (const [index, row] of input.items.entries()) {
|
|
29
29
|
const where = `Row ${String(index + 1)} (${row.title})`;
|
|
30
30
|
for (const keyword of Object.keys(row.values))
|
|
31
31
|
if (!fields.includes(keyword))
|
|
32
|
-
problems.push(`${where} fills {{${keyword}}}, which this template doesn't use (its
|
|
32
|
+
problems.push(`${where} fills {{${keyword}}}, which this template doesn't use (its keywords are ${named}). Remove that column.`);
|
|
33
33
|
if (rowDate(row, plan.dateField) === undefined)
|
|
34
34
|
problems.push(`${where} has no date in {{${plan.dateField}}}. Write it as YYYY-MM-DD, for example 2026-11-14.`);
|
|
35
35
|
}
|
|
@@ -72,11 +72,11 @@ export function claimDesignRun(deps, schedule, now) {
|
|
|
72
72
|
// missed runs. The row stays and is tried again at the same time the next day.
|
|
73
73
|
const scheduledFor = schedule.nextRunAt === null ? now : new Date(schedule.nextRunAt);
|
|
74
74
|
if (current.missedPolicy === "skip" && now.valueOf() - scheduledFor.valueOf() > missedGraceMs)
|
|
75
|
-
return keep("skipped", `Skipped: Slopify wasn't open at ${time} when ${row.title}'s
|
|
75
|
+
return keep("skipped", `Skipped: Slopify wasn't open at ${time} when ${row.title}'s poster was due, and this schedule skips the runs it misses. It tries again at ${time} tomorrow; to make a missed poster as soon as Slopify opens, set Missed run to Run once when Slopify reopens in Templates → Poster templates → ${plan.name} → Schedule.`);
|
|
76
76
|
if (designs === undefined)
|
|
77
77
|
return keep("failed", "Not started: designs aren't available in this copy of Slopify. Restart Slopify; if it keeps happening, reinstall it.");
|
|
78
78
|
if (plan.readyVersion === null)
|
|
79
|
-
return keep("skipped", `Not started: ${plan.name} has no version
|
|
79
|
+
return keep("skipped", `Not started: ${plan.name} has no version approved for schedules. Open it in Templates → Poster templates and press Approve for schedules.`);
|
|
80
80
|
const missing = missingValues(plan.document, row.values);
|
|
81
81
|
if (missing.length > 0) {
|
|
82
82
|
const list = missing.map((name) => `{{${name}}}`).join(", ");
|
|
@@ -85,7 +85,7 @@ export function claimDesignRun(deps, schedule, now) {
|
|
|
85
85
|
const quote = quoteDocument(designs.catalogue(), plan.document);
|
|
86
86
|
if (current.spendLimitCents !== null &&
|
|
87
87
|
(quote.unknown > 0 || quote.high * 100 > current.spendLimitCents))
|
|
88
|
-
return keep("failed", `Not started: ${row.title}'s
|
|
88
|
+
return keep("failed", `Not started: ${row.title}'s poster costs up to $${quote.high.toFixed(2)}${quote.unknown > 0 ? " and some prices are unknown" : ""}, over this schedule's spend limit. Raise the spend limit or choose an image model with a known, lower price.`);
|
|
89
89
|
const started = run("running", null);
|
|
90
90
|
if (!insertRun(deps.db, started))
|
|
91
91
|
return;
|
|
@@ -4,6 +4,7 @@ import { transact } from "../../kernel/db/tx.js";
|
|
|
4
4
|
import { derive } from "../../kernel/runner/graph.js";
|
|
5
5
|
import { projectExists, projectPaused, stagesOf } from "../admission/repo.js";
|
|
6
6
|
import { readStartReceipt } from "../play-drafts/start-repo.js";
|
|
7
|
+
import { setupSectionNames } from "../project-templates/setup-sections.js";
|
|
7
8
|
import { scheduleRunSchema, scheduleSummarySchema, } from "./schema.js";
|
|
8
9
|
const rowSchema = z.object({
|
|
9
10
|
id: z.string(),
|
|
@@ -361,12 +362,12 @@ function parseRun(row) {
|
|
|
361
362
|
const internalRunError = "Slopify hit an internal error while starting this run. The next run will try again; if it keeps happening, use Download diagnostics in Settings and report it.";
|
|
362
363
|
const runErrors = {
|
|
363
364
|
"missing-template": "The template this schedule uses was deleted or changed. Edit the schedule and choose a template again.",
|
|
364
|
-
"unsupported-media":
|
|
365
|
+
"unsupported-media": `This template uses audio, images, a thumbnail, shorts background music or an ambient sound file you supplied, which scheduled runs cannot use. Pick a template that generates these instead, or open this one in Edit template, remove the music under ${setupSectionNames.shorts} (or pick Rain, Fireplace or Wind as the Ambient sound under ${setupSectionNames.video}) and save it again.`,
|
|
365
366
|
"spend-limit": "Not started: the estimated cost was above this schedule's spend limit, or some prices were unknown. Raise the spend limit or choose models with known prices.",
|
|
366
|
-
readiness: "Not started: a provider was not ready, for example a missing API key, model or voice. Check Settings →
|
|
367
|
-
"not-found": "The template's settings are no longer valid, for example a prompt or voice it uses was deleted. Fix the template in
|
|
368
|
-
"invalid-draft": "The template's settings are no longer valid, for example a prompt or voice it uses was deleted. Fix the template in
|
|
369
|
-
"invalid-edit": "The template's settings are no longer valid, for example a prompt or voice it uses was deleted. Fix the template in
|
|
367
|
+
readiness: "Not started: a provider was not ready, for example a missing API key, model or voice. Check Settings → Keys & services; the next run will try again.",
|
|
368
|
+
"not-found": "The template's settings are no longer valid, for example a prompt or voice it uses was deleted. Fix the template in Templates → Edit template and save it.",
|
|
369
|
+
"invalid-draft": "The template's settings are no longer valid, for example a prompt or voice it uses was deleted. Fix the template in Templates → Edit template and save it.",
|
|
370
|
+
"invalid-edit": "The template's settings are no longer valid, for example a prompt or voice it uses was deleted. Fix the template in Templates → Edit template and save it.",
|
|
370
371
|
conflict: internalRunError,
|
|
371
372
|
"pending-start": internalRunError,
|
|
372
373
|
"already-started": internalRunError,
|
|
@@ -309,6 +309,9 @@ export const calendarProjectSchema = z
|
|
|
309
309
|
// A step failed while the run still reads by a wait (`readsByWaitBesideFailure`): listed as a
|
|
310
310
|
// failed item beside what it waits for, as on Home. Older servers leave it out.
|
|
311
311
|
stepFailed: z.literal(true).optional(),
|
|
312
|
+
// When a step that failed on something time can fix runs again by itself, while no step
|
|
313
|
+
// runs (`admission/state-words.ts` retryingAt): it reads Waiting to try again, as on Home.
|
|
314
|
+
retryAt: z.string().optional(),
|
|
312
315
|
// Stages waiting for a CLI plan's limits to reset.
|
|
313
316
|
limitWaits: z
|
|
314
317
|
.array(z
|
|
@@ -108,9 +108,9 @@ async function ask(deps, schedule, options) {
|
|
|
108
108
|
const template = deps.template(schedule.templateId);
|
|
109
109
|
const llm = schedule.topicGeneration.llm ?? template?.document.form.llm;
|
|
110
110
|
if (llm === undefined)
|
|
111
|
-
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.");
|
|
112
112
|
if (deps.topicLlm === undefined)
|
|
113
|
-
return fail("no
|
|
113
|
+
return fail("no AI model is available in this Slopify. Restart Slopify and try again.");
|
|
114
114
|
const wanted = Math.max(generationWanted(schedule), options.force ? spare : 0);
|
|
115
115
|
const known = knownTitles(deps, schedule.id);
|
|
116
116
|
const signal = AbortSignal.any([
|
|
@@ -134,11 +134,11 @@ async function ask(deps, schedule, options) {
|
|
|
134
134
|
if (options.signal?.aborted === true)
|
|
135
135
|
return { ok: false, reason: "failed", message: "Slopify was closing." };
|
|
136
136
|
const detail = errorText(error);
|
|
137
|
-
return fail(`${llm.provider} (${llm.model}) did not answer: ${detail.trim().slice(0, 300)}. Check it in Settings →
|
|
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.`);
|
|
138
138
|
}
|
|
139
139
|
const suggested = parseTopics(answer);
|
|
140
140
|
if (suggested.length === 0)
|
|
141
|
-
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
142
|
// Scored outside the transaction: search may take seconds, and never holds the database.
|
|
143
143
|
const scores = schedule.topicScoring === "off"
|
|
144
144
|
? undefined
|
|
@@ -154,7 +154,7 @@ async function ask(deps, schedule, options) {
|
|
|
154
154
|
? candidates
|
|
155
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)));
|
|
156
156
|
if (fresh.length === 0)
|
|
157
|
-
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.");
|
|
158
158
|
const at = deps.clock.now().toISOString();
|
|
159
159
|
const mode = current.topicGeneration.mode === "hold" ? "hold" : "queue";
|
|
160
160
|
if (scores !== undefined)
|
|
@@ -9,17 +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
18
|
return readJsonSetting(db, defaultsKey, providerDefaultsSchema, {});
|
|
17
19
|
}
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
// First launch: find Claude Code, Codex and Gemini, pick the found ones as Play's defaults, and
|
|
22
|
-
// say a video can be made without any API key. Later launches only report what was found.
|
|
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.
|
|
23
23
|
export async function firstRunStatus(deps, statuses) {
|
|
24
24
|
const detected = statuses
|
|
25
25
|
.filter((status) => status.readiness.kind === "cli")
|
|
@@ -58,28 +58,7 @@ export async function firstRunStatus(deps, statuses) {
|
|
|
58
58
|
};
|
|
59
59
|
writeSetting(deps.db, defaultsKey, JSON.stringify(defaults));
|
|
60
60
|
}
|
|
61
|
-
|
|
62
|
-
.filter((cli) => cli.usable && cli.id !== "codex-image")
|
|
63
|
-
.map((cli) => cli.displayName);
|
|
64
|
-
const systemVoice = statuses.some((status) => status.readiness.kind === "local" && status.readiness.available);
|
|
65
|
-
const narration = systemVoice
|
|
66
|
-
? "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."
|
|
67
|
-
: "Narration still needs a voice provider key, or turn Audio off or upload your own.";
|
|
68
|
-
return {
|
|
69
|
-
firstRun,
|
|
70
|
-
detected,
|
|
71
|
-
defaults,
|
|
72
|
-
message: !firstRun
|
|
73
|
-
? null
|
|
74
|
-
: names.length > 0
|
|
75
|
-
? "You can make a video now, no API keys needed."
|
|
76
|
-
: "No AI command-line tool was found on this computer.",
|
|
77
|
-
detail: !firstRun
|
|
78
|
-
? null
|
|
79
|
-
: names.length > 0
|
|
80
|
-
? `Slopify found ${list(names)} and picked ${names.length === 1 ? "it" : "them"} on Play. ${narration} Keys are optional extras in Settings → Providers.`
|
|
81
|
-
: "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.",
|
|
82
|
-
};
|
|
61
|
+
return { firstRun, detected, defaults };
|
|
83
62
|
}
|
|
84
63
|
async function firstModel(deps, provider, family) {
|
|
85
64
|
try {
|
|
@@ -90,8 +69,3 @@ async function firstModel(deps, provider, family) {
|
|
|
90
69
|
return "";
|
|
91
70
|
}
|
|
92
71
|
}
|
|
93
|
-
function list(names) {
|
|
94
|
-
return names.length <= 1
|
|
95
|
-
? (names[0] ?? "")
|
|
96
|
-
: `${names.slice(0, -1).join(", ")} and ${names[names.length - 1]}`;
|
|
97
|
-
}
|
|
@@ -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,
|
|
@@ -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",
|
|
@@ -70,8 +70,10 @@ export const providers = [
|
|
|
70
70
|
binary: "agy",
|
|
71
71
|
versionArgs: ["--version"],
|
|
72
72
|
},
|
|
73
|
-
|
|
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.
|
|
74
75
|
{ id: "openai-tts", family: "tts", displayName: "OpenAI", auth: "key" },
|
|
76
|
+
{ id: "elevenlabs", family: "tts", displayName: "ElevenLabs", auth: "key" },
|
|
75
77
|
{ id: "cartesia", family: "tts", displayName: "Cartesia", auth: "key" },
|
|
76
78
|
{ id: "inworld", family: "tts", displayName: "Inworld", auth: "key" },
|
|
77
79
|
{ id: "system-voice", family: "tts", displayName: "System voice", auth: "local" },
|
|
@@ -56,7 +56,7 @@ export function saveSettings(deps, settings) {
|
|
|
56
56
|
if (!subtitleModelValue.safeParse(settings.subtitleModel).success) {
|
|
57
57
|
fields.push({
|
|
58
58
|
field: "subtitleModel",
|
|
59
|
-
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
60
|
});
|
|
61
61
|
}
|
|
62
62
|
if (!videoEncoderValue.safeParse(settings.videoEncoder).success) {
|
|
@@ -74,7 +74,7 @@ export function saveSettings(deps, settings) {
|
|
|
74
74
|
if (!renderPriorityValue.safeParse(settings.renderPriority).success) {
|
|
75
75
|
fields.push({
|
|
76
76
|
field: "renderPriority",
|
|
77
|
-
message: "Choose Normal or Background in Settings → Production defaults →
|
|
77
|
+
message: "Choose Normal or Background in Settings → Production defaults → Encoding priority.",
|
|
78
78
|
});
|
|
79
79
|
}
|
|
80
80
|
if (fields.length > 0) {
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { editSettings } from "../../kernel/ports/settings-places.js";
|
|
1
2
|
import { clipBounds } from "./pick.js";
|
|
2
3
|
// Shorts kept through a change of their moments: a count raised or lowered, a new intro or a
|
|
3
4
|
// narration edit that moves the timing. A kept short is remembered by its sentences' text, so
|
|
@@ -87,5 +88,5 @@ export function lostMessage(lost, kept) {
|
|
|
87
88
|
return short === undefined ? `short ${String(at)}` : `short ${String(at)} ("${short.title}")`;
|
|
88
89
|
})
|
|
89
90
|
.join(", ");
|
|
90
|
-
return `The narration of ${names} changed, so its moment can't be kept.
|
|
91
|
+
return `The narration of ${names} changed, so its moment can't be kept. Untick it under Keep finished shorts in ${editSettings.shorts}, so a new moment is picked for it, save, then use Choose what to make again (in the project's More menu).`;
|
|
91
92
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { jsonBetween } from "../../kernel/json-answer.js";
|
|
3
|
+
import { editSettings } from "../../kernel/ports/settings-places.js";
|
|
3
4
|
import { youtubeTimestamp } from "../youtube/timestamps.js";
|
|
4
5
|
import { shortHashtagsMax, shortHashtagsMin, shortTitleMax } from "./model.js";
|
|
5
6
|
export const shortPickSchema = z.object({
|
|
@@ -121,7 +122,7 @@ const answerSchema = z.array(z.object({
|
|
|
121
122
|
hashtags: z.array(z.string()),
|
|
122
123
|
why: z.string().optional(),
|
|
123
124
|
}));
|
|
124
|
-
const fix =
|
|
125
|
+
const fix = `Try again, or choose another model in ${editSettings.writer}.`;
|
|
125
126
|
// Timing noise: the length rules are checked to the hundredth of a second.
|
|
126
127
|
const tolerance = 0.01;
|
|
127
128
|
// Reads the model's answer and keeps the clips that follow the rules: in range, the right
|
|
@@ -221,8 +222,8 @@ export function checkPicks(text, sentences, limits) {
|
|
|
221
222
|
export function noPicksMessage(problems, limits) {
|
|
222
223
|
const shortVideo = limits.durationSeconds < limits.minSeconds;
|
|
223
224
|
if (shortVideo)
|
|
224
|
-
return `The narration is ${String(Math.round(limits.durationSeconds))} seconds long, shorter than the ${String(limits.minSeconds)}-second minimum for a short, so there is nothing to cut. Lower the shortest
|
|
225
|
-
return `The AI model didn't pick any clip Slopify could use as a short, twice (${problems[0] ?? "no clips were listed"}). Try again; if it keeps happening, widen the
|
|
225
|
+
return `The narration is ${String(Math.round(limits.durationSeconds))} seconds long, shorter than the ${String(limits.minSeconds)}-second minimum for a short, so there is nothing to cut. Lower the shortest Length in ${editSettings.shorts}, then Try again.`;
|
|
226
|
+
return `The AI model didn't pick any clip Slopify could use as a short, twice (${problems[0] ?? "no clips were listed"}). Try again; if it keeps happening, widen the Length range in ${editSettings.shorts}, or choose another model in ${editSettings.writer}.`;
|
|
226
227
|
}
|
|
227
228
|
function round(seconds) {
|
|
228
229
|
return Math.round(seconds * 1000) / 1000;
|