@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
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
import { errorText } from "../../kernel/errors.js";
|
|
2
|
+
import { defaultChannelId } from "../channels/model.js";
|
|
3
|
+
import { projectChannels, templateChannelId } from "../channels/repo.js";
|
|
4
|
+
import { choiceSites, retiredModelUsage, slotLabels } from "../model-upkeep/usage.js";
|
|
5
|
+
import { weekSummary } from "../run-cost/week.js";
|
|
6
|
+
import { calendarRange } from "../schedules/agenda.js";
|
|
7
|
+
import { scheduleRows } from "../schedules/repo.js";
|
|
8
|
+
import { bufferForecast } from "./buffer.js";
|
|
9
|
+
import { forecastDays } from "./model.js";
|
|
10
|
+
import { readSnoozes, weeklyNotifyOf } from "./repo.js";
|
|
11
|
+
import { warningsOf } from "./warnings.js";
|
|
12
|
+
import { weeklyReport } from "./weekly.js";
|
|
13
|
+
const dayMs = 24 * 60 * 60_000;
|
|
14
|
+
// ceiling: one probe that hangs is skipped after this, so the hourly check always ends.
|
|
15
|
+
export const probeTimeoutMs = 15_000;
|
|
16
|
+
const clis = {
|
|
17
|
+
"claude-code": { name: "Claude Code", signIn: "claude auth login" },
|
|
18
|
+
codex: { name: "Codex", signIn: "codex login" },
|
|
19
|
+
gemini: { name: "Antigravity CLI", signIn: "agy" },
|
|
20
|
+
};
|
|
21
|
+
const cliOfProvider = (provider) => provider === "codex-image"
|
|
22
|
+
? "codex"
|
|
23
|
+
: provider === "claude-code" || provider === "codex" || provider === "gemini"
|
|
24
|
+
? provider
|
|
25
|
+
: undefined;
|
|
26
|
+
const live = (schedule) => schedule.deletedAt === null && (schedule.status === "active" || schedule.status === "paused");
|
|
27
|
+
// A design schedule makes designed pieces from dated rows: no video fills a slot, no topic runs
|
|
28
|
+
// out and no CLI writes it, so the forecast and its warnings leave it out entirely.
|
|
29
|
+
const makesVideos = (schedule) => schedule.templateKind !== "design";
|
|
30
|
+
// The CLIs an active schedule's template or topic finding chooses: the only ones worth asking.
|
|
31
|
+
export function clisInUse(db) {
|
|
32
|
+
const schedules = scheduleRows(db).filter((one) => one.deletedAt === null && one.status === "active" && makesVideos(one));
|
|
33
|
+
const templates = new Set(schedules.map((one) => one.templateId));
|
|
34
|
+
const ids = new Set(schedules.map((one) => one.id));
|
|
35
|
+
const found = new Set();
|
|
36
|
+
for (const site of choiceSites(db)) {
|
|
37
|
+
const used = (site.kind === "template" && templates.has(site.id)) ||
|
|
38
|
+
(site.kind === "schedule" && ids.has(site.id));
|
|
39
|
+
if (!used)
|
|
40
|
+
continue;
|
|
41
|
+
for (const choice of Object.values(site.choices)) {
|
|
42
|
+
const cli = choice === undefined ? undefined : cliOfProvider(choice.provider);
|
|
43
|
+
if (cli !== undefined)
|
|
44
|
+
found.add(cli);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
for (const schedule of schedules) {
|
|
48
|
+
const cli = cliOfProvider(schedule.topicGeneration.llm?.provider ?? "");
|
|
49
|
+
if (schedule.topicGeneration.mode !== "off" && cli !== undefined)
|
|
50
|
+
found.add(cli);
|
|
51
|
+
}
|
|
52
|
+
return [...found].sort();
|
|
53
|
+
}
|
|
54
|
+
async function within(deps, what, probe) {
|
|
55
|
+
const signal = AbortSignal.timeout(probeTimeoutMs);
|
|
56
|
+
try {
|
|
57
|
+
return await Promise.race([
|
|
58
|
+
probe(signal),
|
|
59
|
+
new Promise((_resolve, reject) => {
|
|
60
|
+
signal.addEventListener("abort", () => reject(new Error(`${what} took too long`)), {
|
|
61
|
+
once: true,
|
|
62
|
+
});
|
|
63
|
+
}),
|
|
64
|
+
]);
|
|
65
|
+
}
|
|
66
|
+
catch (error) {
|
|
67
|
+
deps.log.write("warn", "autopilot.probe", { detail: `${what}: ${errorText(error)}` });
|
|
68
|
+
return undefined;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
export async function collectProbes(deps) {
|
|
72
|
+
const asked = await Promise.all(clisInUse(deps.db).map(async (id) => {
|
|
73
|
+
const login = deps.cliLogin;
|
|
74
|
+
if (login === undefined)
|
|
75
|
+
return undefined;
|
|
76
|
+
const answer = await within(deps, `${clis[id].name} sign-in`, (signal) => login(id, signal));
|
|
77
|
+
return answer === undefined ? undefined : [id, answer];
|
|
78
|
+
}));
|
|
79
|
+
const key = deps.openRouterKey;
|
|
80
|
+
const disk = deps.disk;
|
|
81
|
+
const [openRouter, space] = await Promise.all([
|
|
82
|
+
key === undefined ? undefined : within(deps, "OpenRouter key", key),
|
|
83
|
+
disk === undefined ? undefined : within(deps, "free disk space", () => disk()),
|
|
84
|
+
]);
|
|
85
|
+
return {
|
|
86
|
+
checkedAt: deps.clock.now().toISOString(),
|
|
87
|
+
clis: new Map(asked.flatMap((one) => (one === undefined ? [] : [one]))),
|
|
88
|
+
openRouter: openRouter ?? null,
|
|
89
|
+
disk: space ?? null,
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
// The hourly check's last answers, held in memory: Home reads them and never waits for a CLI.
|
|
93
|
+
export function createAutopilotHealth(deps) {
|
|
94
|
+
let latest;
|
|
95
|
+
let running;
|
|
96
|
+
return {
|
|
97
|
+
latest: () => latest,
|
|
98
|
+
refresh: () => {
|
|
99
|
+
running ??= collectProbes(deps)
|
|
100
|
+
.then((probes) => {
|
|
101
|
+
latest = probes;
|
|
102
|
+
return probes;
|
|
103
|
+
})
|
|
104
|
+
.finally(() => {
|
|
105
|
+
running = undefined;
|
|
106
|
+
});
|
|
107
|
+
return running;
|
|
108
|
+
},
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
// Home's autopilot block for every channel, or one: the forecast and warnings from the
|
|
112
|
+
// database now and the probes' last answers, and the last seven days.
|
|
113
|
+
export function autopilotView(deps, probes, channelId) {
|
|
114
|
+
const now = deps.clock.now();
|
|
115
|
+
const until = new Date(now.valueOf() + forecastDays * dayMs);
|
|
116
|
+
const inChannel = (templateId) => channelId === undefined || templateChannelId(deps.db, templateId) === channelId;
|
|
117
|
+
const schedules = scheduleRows(deps.db).filter((one) => live(one) && makesVideos(one) && inChannel(one.templateId));
|
|
118
|
+
const ahead = calendarRange(deps, now, until);
|
|
119
|
+
// Finished videos not uploaded: the calendar's longest look back.
|
|
120
|
+
const behind = calendarRange(deps, new Date(now.valueOf() - 91 * dayMs), now);
|
|
121
|
+
const channels = projectChannels(deps.db);
|
|
122
|
+
const ready = behind.ok
|
|
123
|
+
? behind.value.projects.filter((one) => one.readyToUpload === true &&
|
|
124
|
+
one.setAside !== true &&
|
|
125
|
+
(channelId === undefined || (channels.get(one.id) ?? defaultChannelId) === channelId)).length
|
|
126
|
+
: 0;
|
|
127
|
+
// A run its template has no version for is skipped: it fills no slot.
|
|
128
|
+
const runs = ahead.ok
|
|
129
|
+
? ahead.value.runs.filter((run) => makesVideos(run) && inChannel(run.templateId) && run.notReady !== true)
|
|
130
|
+
: [];
|
|
131
|
+
const forecast = bufferForecast({
|
|
132
|
+
now,
|
|
133
|
+
until,
|
|
134
|
+
runs,
|
|
135
|
+
ready,
|
|
136
|
+
schedules: schedules.map((one) => ({
|
|
137
|
+
id: one.id,
|
|
138
|
+
name: one.name,
|
|
139
|
+
paused: one.status === "paused",
|
|
140
|
+
queued: one.items.length,
|
|
141
|
+
generation: one.topicGeneration.mode,
|
|
142
|
+
generationFailing: one.topics.error !== null,
|
|
143
|
+
cadence: one.cadence,
|
|
144
|
+
timezone: one.timezone,
|
|
145
|
+
})),
|
|
146
|
+
});
|
|
147
|
+
const active = new Set(schedules.filter((one) => one.status === "active").map((one) => one.id));
|
|
148
|
+
const templates = new Set(schedules.filter((one) => active.has(one.id)).map((one) => one.templateId));
|
|
149
|
+
const catalogue = deps.catalogue?.();
|
|
150
|
+
const signals = {
|
|
151
|
+
clis: [...(probes?.clis ?? new Map())].map(([id, login]) => ({
|
|
152
|
+
id,
|
|
153
|
+
...clis[id],
|
|
154
|
+
login,
|
|
155
|
+
})),
|
|
156
|
+
plans: weekSummary(deps.db, now.toISOString()).plans,
|
|
157
|
+
openRouter: probes?.openRouter ?? null,
|
|
158
|
+
disk: probes?.disk ?? null,
|
|
159
|
+
schedules: schedules.map((one) => ({
|
|
160
|
+
id: one.id,
|
|
161
|
+
name: one.name,
|
|
162
|
+
paused: one.status === "paused",
|
|
163
|
+
queued: one.items.length,
|
|
164
|
+
generation: one.topicGeneration.mode,
|
|
165
|
+
topicsError: one.topics.error,
|
|
166
|
+
notReadyTemplate: deps.notReady?.(one.templateId) === undefined
|
|
167
|
+
? null
|
|
168
|
+
: (deps.template(one.templateId)?.name ?? "this schedule's template"),
|
|
169
|
+
})),
|
|
170
|
+
retired: catalogue === undefined
|
|
171
|
+
? []
|
|
172
|
+
: retiredModelUsage(deps.db, catalogue).flatMap((use) => (use.kind === "template" && templates.has(use.id)) ||
|
|
173
|
+
(use.kind === "schedule" && active.has(use.id))
|
|
174
|
+
? [
|
|
175
|
+
{
|
|
176
|
+
kind: use.kind,
|
|
177
|
+
id: use.id,
|
|
178
|
+
name: use.name,
|
|
179
|
+
slot: slotLabels[use.slot],
|
|
180
|
+
model: use.model,
|
|
181
|
+
why: use.why,
|
|
182
|
+
replacement: use.replacement?.name ?? null,
|
|
183
|
+
},
|
|
184
|
+
]
|
|
185
|
+
: []),
|
|
186
|
+
};
|
|
187
|
+
return {
|
|
188
|
+
forecast,
|
|
189
|
+
warnings: warningsOf(signals, readSnoozes(deps.db, now), now),
|
|
190
|
+
checkedAt: probes?.checkedAt ?? null,
|
|
191
|
+
weekly: weeklyReport(deps.db, new Date(now.valueOf() - 7 * dayMs).toISOString(), now.toISOString(), channelId),
|
|
192
|
+
weeklyNotify: weeklyNotifyOf(deps.db),
|
|
193
|
+
};
|
|
194
|
+
}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { diskLowBytes, diskLowShare, fewTopicsLeft, lowPercent, } from "./model.js";
|
|
2
|
+
const gb = (bytes) => `${(bytes / 1024 ** 3).toFixed(1)} GB`;
|
|
3
|
+
const dollars = (value) => `$${value.toFixed(2)}`;
|
|
4
|
+
// Each warning says what is wrong, why it stops the autopilot, and where the fix is. A
|
|
5
|
+
// warning hidden with Hide stays hidden until its snooze ends.
|
|
6
|
+
export function warningsOf(signals, snoozes, now) {
|
|
7
|
+
const found = [];
|
|
8
|
+
for (const cli of signals.clis)
|
|
9
|
+
if (cli.login === "signed-out")
|
|
10
|
+
found.push({
|
|
11
|
+
key: `cli-signed-out:${cli.id}`,
|
|
12
|
+
severity: "problem",
|
|
13
|
+
text: `${cli.name} is signed out, so scheduled runs that use it will fail. Open a terminal on this computer, run "${cli.signIn}" and sign in.`,
|
|
14
|
+
fix: { kind: "settings", section: "providers", label: "Open Providers" },
|
|
15
|
+
});
|
|
16
|
+
for (const plan of signals.plans) {
|
|
17
|
+
const weekly = plan.weeklyPercent ?? 0;
|
|
18
|
+
const fiveHour = plan.fiveHourPercent ?? 0;
|
|
19
|
+
if (weekly < lowPercent && fiveHour < lowPercent)
|
|
20
|
+
continue;
|
|
21
|
+
const window = weekly >= fiveHour ? "weekly" : "5-hour";
|
|
22
|
+
found.push({
|
|
23
|
+
key: `plan-low:${plan.name}`,
|
|
24
|
+
severity: "warning",
|
|
25
|
+
text: `${plan.name} has used ${String(Math.round(Math.max(weekly, fiveHour)))}% of its ${window} limit, so scheduled runs on it may wait until the limit resets. Check when it resets, or move a template to another provider.`,
|
|
26
|
+
fix: { kind: "settings", section: "usage", label: "See plan usage" },
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
const key = signals.openRouter;
|
|
30
|
+
if (key !== null && key.limit > 0 && key.remaining / key.limit < 1 - lowPercent / 100)
|
|
31
|
+
found.push({
|
|
32
|
+
key: "openrouter-low",
|
|
33
|
+
severity: key.remaining <= 0 ? "problem" : "warning",
|
|
34
|
+
text: `Your OpenRouter key has ${dollars(Math.max(0, key.remaining))} left of its ${dollars(key.limit)} limit, so runs that use OpenRouter stop when it runs out. Raise the key's limit or add credit on openrouter.ai.`,
|
|
35
|
+
fix: { kind: "settings", section: "providers", label: "Open Providers" },
|
|
36
|
+
});
|
|
37
|
+
const disk = signals.disk;
|
|
38
|
+
if (disk !== null &&
|
|
39
|
+
(disk.freeBytes < diskLowBytes ||
|
|
40
|
+
(disk.totalBytes > 0 && disk.freeBytes / disk.totalBytes < diskLowShare)))
|
|
41
|
+
found.push({
|
|
42
|
+
key: "disk-low",
|
|
43
|
+
severity: disk.freeBytes < diskLowBytes ? "problem" : "warning",
|
|
44
|
+
text: `Only ${gb(disk.freeBytes)} is free on the disk Slopify saves to, so renders and downloads may fail. Free some space, or delete old versions and empty the Trash in Settings → Backup & storage.`,
|
|
45
|
+
fix: { kind: "settings", section: "storage", label: "Open Backup & storage" },
|
|
46
|
+
});
|
|
47
|
+
for (const schedule of signals.schedules) {
|
|
48
|
+
if (schedule.paused)
|
|
49
|
+
continue;
|
|
50
|
+
if (schedule.notReadyTemplate !== null)
|
|
51
|
+
found.push({
|
|
52
|
+
key: `template-not-ready:${schedule.id}`,
|
|
53
|
+
severity: "problem",
|
|
54
|
+
text: `“${schedule.name}” skips its runs because its template “${schedule.notReadyTemplate}” has no version approved for schedules. Open it from Templates with Edit template, then choose More → Approve for schedules.`,
|
|
55
|
+
fix: { kind: "templates", label: "Open Templates" },
|
|
56
|
+
});
|
|
57
|
+
if (schedule.topicsError !== null && schedule.generation !== "off")
|
|
58
|
+
found.push({
|
|
59
|
+
key: `topics-failing:${schedule.id}`,
|
|
60
|
+
severity: "problem",
|
|
61
|
+
text: `“${schedule.name}” couldn't find new topics: ${schedule.topicsError} Its queue won't refill until that is fixed. Open the schedule, fix what the error says, then find topics again.`,
|
|
62
|
+
fix: { kind: "schedule", scheduleId: schedule.id, label: "Open the schedule" },
|
|
63
|
+
});
|
|
64
|
+
if (schedule.generation === "off" && schedule.queued > 0 && schedule.queued < fewTopicsLeft)
|
|
65
|
+
found.push({
|
|
66
|
+
key: `few-topics:${schedule.id}:${String(schedule.queued)}`,
|
|
67
|
+
severity: "warning",
|
|
68
|
+
text: `“${schedule.name}” has ${schedule.queued === 1 ? "1 topic" : `${String(schedule.queued)} topics`} left and doesn't find new ones, so it stops after ${schedule.queued === 1 ? "its next run" : `its next ${String(schedule.queued)} runs`}. Add topics to its queue, or turn on topic generation.`,
|
|
69
|
+
fix: { kind: "schedule", scheduleId: schedule.id, label: "Open the schedule" },
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
for (const use of signals.retired)
|
|
73
|
+
found.push({
|
|
74
|
+
key: `retired:${use.kind}:${use.id}:${use.model}`,
|
|
75
|
+
severity: "warning",
|
|
76
|
+
text: `The ${use.slot.toLowerCase()} “${use.model}” in ${use.kind} “${use.name}” is ${use.why === "retired" ? "being retired" : "no longer offered"}, so its runs will fail once it is gone. Switch it to ${use.replacement === null ? "another model" : `“${use.replacement}”`}.`,
|
|
77
|
+
fix: use.kind === "schedule"
|
|
78
|
+
? { kind: "schedule", scheduleId: use.id, label: "Open the schedule" }
|
|
79
|
+
: { kind: "templates", label: "Open Templates" },
|
|
80
|
+
});
|
|
81
|
+
const at = now.toISOString();
|
|
82
|
+
return found
|
|
83
|
+
.filter((warning) => (snoozes.get(warning.key) ?? "") <= at)
|
|
84
|
+
.toSorted((a, b) => Number(b.severity === "problem") - Number(a.severity === "problem"));
|
|
85
|
+
}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
/// <reference lib="esnext.temporal" />
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
import { liveProject } from "../admission/repo.js";
|
|
4
|
+
import { defaultChannelId } from "../channels/model.js";
|
|
5
|
+
import { projectChannels } from "../channels/repo.js";
|
|
6
|
+
import { weekSummary } from "../run-cost/week.js";
|
|
7
|
+
import { validTimeZone } from "../schedules/calendar.js";
|
|
8
|
+
import { notTemplateSample } from "../template-studio/repo.js";
|
|
9
|
+
// ceiling: the report names this many failed steps; the rest are counted.
|
|
10
|
+
const failedShown = 10;
|
|
11
|
+
// The hour on Monday, in the person's time zone, from which the week's report is sent.
|
|
12
|
+
const sendHour = 9;
|
|
13
|
+
const stageNames = {
|
|
14
|
+
research: "Research",
|
|
15
|
+
article: "Article",
|
|
16
|
+
audio: "Narration",
|
|
17
|
+
images: "Images",
|
|
18
|
+
thumbnail: "Thumbnail",
|
|
19
|
+
video: "Video",
|
|
20
|
+
document: "Document",
|
|
21
|
+
};
|
|
22
|
+
const failedRow = z.object({
|
|
23
|
+
project_id: z.string(),
|
|
24
|
+
title: z.string(),
|
|
25
|
+
kind: z.string(),
|
|
26
|
+
failure_reason: z.string().nullable(),
|
|
27
|
+
});
|
|
28
|
+
// The last week in one read: what finished and what failed, what it cost and where the plans
|
|
29
|
+
// stand. The same stored calls Home's This week reads; nothing is estimated.
|
|
30
|
+
export function weeklyReport(db, since, until, channelId) {
|
|
31
|
+
const summary = weekSummary(db, since, channelId);
|
|
32
|
+
const channels = channelId === undefined ? undefined : projectChannels(db);
|
|
33
|
+
const failed = db
|
|
34
|
+
.prepare(`SELECT stages.project_id, projects.title, stages.kind, stages.failure_reason
|
|
35
|
+
FROM stages JOIN projects ON projects.id=stages.project_id
|
|
36
|
+
WHERE stages.state='failed' AND stages.finished_at>=? AND ${liveProject()}
|
|
37
|
+
AND ${notTemplateSample()}
|
|
38
|
+
ORDER BY stages.finished_at DESC, stages.id`)
|
|
39
|
+
.all(since)
|
|
40
|
+
.map((raw) => failedRow.parse(raw))
|
|
41
|
+
.filter((row) => channels === undefined || (channels.get(row.project_id) ?? defaultChannelId) === channelId)
|
|
42
|
+
.map((row) => ({
|
|
43
|
+
projectId: row.project_id,
|
|
44
|
+
title: row.title.trim() === "" ? "Untitled project" : row.title,
|
|
45
|
+
stage: stageNames[row.kind] ?? row.kind,
|
|
46
|
+
reason: row.failure_reason ?? "It stopped without a reason.",
|
|
47
|
+
}));
|
|
48
|
+
return {
|
|
49
|
+
since,
|
|
50
|
+
until,
|
|
51
|
+
finished: summary.videos,
|
|
52
|
+
failed: failed.slice(0, failedShown),
|
|
53
|
+
failedMore: Math.max(0, failed.length - failedShown),
|
|
54
|
+
cost: summary.cost,
|
|
55
|
+
unpriced: summary.unpriced,
|
|
56
|
+
apiEquivalent: summary.apiEquivalent,
|
|
57
|
+
plans: summary.plans.map((plan) => ({
|
|
58
|
+
name: plan.name,
|
|
59
|
+
weeklyPercent: plan.weeklyPercent,
|
|
60
|
+
fiveHourPercent: plan.fiveHourPercent,
|
|
61
|
+
})),
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
// The ISO week a moment falls in, in the person's time zone: "2026-W42".
|
|
65
|
+
export function isoWeekOf(now, timeZone) {
|
|
66
|
+
const zone = validTimeZone(timeZone) ? timeZone : "UTC";
|
|
67
|
+
const date = Temporal.Instant.fromEpochMilliseconds(now.valueOf()).toZonedDateTimeISO(zone);
|
|
68
|
+
return `${String(date.yearOfWeek)}-W${String(date.weekOfYear).padStart(2, "0")}`;
|
|
69
|
+
}
|
|
70
|
+
// Whether this week's report is still to be sent: from Monday 09:00 in the person's time zone,
|
|
71
|
+
// once a week. A Slopify that was off on Monday sends it when it next runs that week.
|
|
72
|
+
export function weeklyDue(now, timeZone, sentFor) {
|
|
73
|
+
const zone = validTimeZone(timeZone) ? timeZone : "UTC";
|
|
74
|
+
const date = Temporal.Instant.fromEpochMilliseconds(now.valueOf()).toZonedDateTimeISO(zone);
|
|
75
|
+
const week = isoWeekOf(now, zone);
|
|
76
|
+
const started = date.dayOfWeek > 1 || date.hour >= sendHour;
|
|
77
|
+
return { due: started && sentFor !== week, week };
|
|
78
|
+
}
|
|
79
|
+
const money = (value) => `$${value.toFixed(2)}`;
|
|
80
|
+
export function weeklyNoticeText(report) {
|
|
81
|
+
const failures = report.failed.length + report.failedMore;
|
|
82
|
+
const plans = report.plans
|
|
83
|
+
.filter((plan) => plan.weeklyPercent !== null)
|
|
84
|
+
.map((plan) => `${plan.name} ${String(Math.round(plan.weeklyPercent ?? 0))}% of its week`);
|
|
85
|
+
const named = report.failed
|
|
86
|
+
.slice(0, 3)
|
|
87
|
+
.map((one) => `“${one.title}” (${one.stage}): ${one.reason.replace(/[.!?\s]+$/, "")}`);
|
|
88
|
+
return {
|
|
89
|
+
headline: `Slopify last week: ${report.finished === 1 ? "1 video" : `${String(report.finished)} videos`} made, ${failures === 1 ? "1 step" : `${String(failures)} steps`} failed`,
|
|
90
|
+
detail: [
|
|
91
|
+
`Cost ${money(report.cost)}${report.unpriced > 0 ? ` plus ${String(report.unpriced)} calls with no known price` : ""}.`,
|
|
92
|
+
plans.length === 0 ? "" : `Plans: ${plans.join(", ")}.`,
|
|
93
|
+
named.length === 0
|
|
94
|
+
? ""
|
|
95
|
+
: `Failed: ${named.join("; ")}${failures > named.length ? `; and ${String(failures - named.length)} more` : ""}. Open each project and use the button beside the failed step.`,
|
|
96
|
+
]
|
|
97
|
+
.filter((line) => line !== "")
|
|
98
|
+
.join(" "),
|
|
99
|
+
};
|
|
100
|
+
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { constants } from "node:fs";
|
|
2
2
|
import { lstat, mkdir, open, readdir, rename, rm } from "node:fs/promises";
|
|
3
3
|
import { join } from "node:path";
|
|
4
|
+
import { errnoCode } from "../../kernel/errors.js";
|
|
4
5
|
// Only files whose whole name matches these are this feature's. Pruning and the leftover sweep
|
|
5
6
|
// touch nothing else in the folder: not an Export everything download
|
|
6
7
|
// (slopify-backup-2026-09-27.tar), not a file the user renamed, not a symlink.
|
|
@@ -92,7 +93,7 @@ export async function listBackups(folder) {
|
|
|
92
93
|
names = await readdir(folder);
|
|
93
94
|
}
|
|
94
95
|
catch (error) {
|
|
95
|
-
if (
|
|
96
|
+
if (errnoCode(error) === "ENOENT")
|
|
96
97
|
return [];
|
|
97
98
|
throw error;
|
|
98
99
|
}
|
|
@@ -128,8 +129,3 @@ export async function removeLeftovers(folder) {
|
|
|
128
129
|
}
|
|
129
130
|
return removed;
|
|
130
131
|
}
|
|
131
|
-
export function codeOf(error) {
|
|
132
|
-
return error instanceof Error && "code" in error && typeof error.code === "string"
|
|
133
|
-
? error.code
|
|
134
|
-
: undefined;
|
|
135
|
-
}
|
|
@@ -1,28 +1,16 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { readJsonSetting, writeSetting } from "../settings/repo.js";
|
|
2
2
|
import { backupStatusSchema, configKey, defaultConfig, emptyStatus, statusKey, storedConfigSchema, } from "./model.js";
|
|
3
3
|
// A value this build cannot read (hand-edited, or written by a newer Slopify) falls back to
|
|
4
4
|
// the default rather than stopping the app; the next save writes a good one.
|
|
5
5
|
export function readConfig(db, fallbackTimeZone) {
|
|
6
|
-
|
|
7
|
-
return parsed.success ? parsed.data : defaultConfig(fallbackTimeZone);
|
|
6
|
+
return readJsonSetting(db, configKey, storedConfigSchema, defaultConfig(fallbackTimeZone));
|
|
8
7
|
}
|
|
9
8
|
export function writeConfig(db, config) {
|
|
10
9
|
writeSetting(db, configKey, JSON.stringify(storedConfigSchema.parse(config)));
|
|
11
10
|
}
|
|
12
11
|
export function readStatus(db) {
|
|
13
|
-
|
|
14
|
-
return parsed.success ? parsed.data : emptyStatus;
|
|
12
|
+
return readJsonSetting(db, statusKey, backupStatusSchema, emptyStatus);
|
|
15
13
|
}
|
|
16
14
|
export function writeStatus(db, status) {
|
|
17
15
|
writeSetting(db, statusKey, JSON.stringify(backupStatusSchema.parse(status)));
|
|
18
16
|
}
|
|
19
|
-
function parseJson(value) {
|
|
20
|
-
if (value === undefined)
|
|
21
|
-
return undefined;
|
|
22
|
-
try {
|
|
23
|
-
return JSON.parse(value);
|
|
24
|
-
}
|
|
25
|
-
catch {
|
|
26
|
-
return undefined;
|
|
27
|
-
}
|
|
28
|
-
}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { statfs } from "node:fs/promises";
|
|
2
|
+
import { errnoCode } from "../../kernel/errors.js";
|
|
2
3
|
import { BackupBusyError, planBackup, streamBackup, } from "../storage/backup-export.js";
|
|
3
4
|
import { defaultBackupsDir } from "../storage/layout.js";
|
|
4
|
-
import { backupFileName,
|
|
5
|
+
import { backupFileName, ensureFolder, listBackups, pruneBackups, removeLeftovers, writeAtomically, } from "./files.js";
|
|
5
6
|
import { effectiveFolder, folderProblem, hostFolder } from "./folder.js";
|
|
6
7
|
import { readConfig, readStatus, writeConfig, writeStatus } from "./repo.js";
|
|
7
8
|
import { decideBackup, nextSlot } from "./schedule.js";
|
|
@@ -59,7 +60,7 @@ export function createBackupService(deps) {
|
|
|
59
60
|
}
|
|
60
61
|
catch (error) {
|
|
61
62
|
deps.log.write("warn", "backups.prune", { detail: String(error) });
|
|
62
|
-
detail = `The backup was saved, but older backups in ${folder} could not be removed (${
|
|
63
|
+
detail = `The backup was saved, but older backups in ${folder} could not be removed (${errnoCode(error) ?? "unknown error"}), so more than ${settings.keep} are kept. Check Slopify can delete files in that folder, or pick another Folder in Settings → Backup & storage.`;
|
|
63
64
|
}
|
|
64
65
|
record({
|
|
65
66
|
...attempt,
|
|
@@ -272,17 +273,17 @@ function named(projects) {
|
|
|
272
273
|
.join(", ");
|
|
273
274
|
return projects.length > 3 ? `${titles} and ${projects.length - 3} more` : titles;
|
|
274
275
|
}
|
|
275
|
-
// What failed, why, and what to change - the sentence Settings →
|
|
276
|
+
// What failed, why, and what to change - the sentence Settings → Backup & storage shows.
|
|
276
277
|
export function explain(error, folder, retries, signal) {
|
|
277
|
-
const fix = "Pick another Folder in Settings →
|
|
278
|
+
const fix = "Pick another Folder in Settings → Backup & storage, or leave it empty to use the default";
|
|
278
279
|
if (signal?.aborted === true && error === signal.reason)
|
|
279
280
|
return "The backup was not saved: Slopify stopped while writing it, and the unfinished file was removed. It runs again after Slopify starts.";
|
|
280
281
|
if (error instanceof NoSpaceError)
|
|
281
|
-
return `The backup was not saved: it needs about ${bytes(error.needed)}, but the disk holding ${folder} has ${bytes(error.free)} free. Free up space there, lower "Keep last" in Settings →
|
|
282
|
-
switch (
|
|
282
|
+
return `The backup was not saved: it needs about ${bytes(error.needed)}, but the disk holding ${folder} has ${bytes(error.free)} free. Free up space there, lower "Keep last" in Settings → Backup & storage, or pick another Folder.`;
|
|
283
|
+
switch (errnoCode(error)) {
|
|
283
284
|
case "ENOSPC":
|
|
284
285
|
case "EDQUOT":
|
|
285
|
-
return `The backup was not saved: the disk holding ${folder} ran out of space. Free up space there, lower "Keep last" in Settings →
|
|
286
|
+
return `The backup was not saved: the disk holding ${folder} ran out of space. Free up space there, lower "Keep last" in Settings → Backup & storage, or pick another Folder.`;
|
|
286
287
|
case "EACCES":
|
|
287
288
|
case "EPERM":
|
|
288
289
|
case "EROFS":
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { errorText } from "../../kernel/errors.js";
|
|
1
2
|
import { castImageMaxBytes, castImagesPerMember, sniffImage, storeImageBlob } from "./images.js";
|
|
2
3
|
import { castMemberById } from "./repo.js";
|
|
3
4
|
import { castGenerateSchema } from "./schema.js";
|
|
@@ -58,9 +59,9 @@ export function generateCastImage(deps, memberId, input) {
|
|
|
58
59
|
.prepare("UPDATE cast_images SET state='ready',sha256=? WHERE id=? AND state='generating'")
|
|
59
60
|
.run(sha256, id);
|
|
60
61
|
}, (error) => {
|
|
61
|
-
const reason =
|
|
62
|
+
const reason = errorText(error);
|
|
62
63
|
deps.log?.write("warn", "channels.cast-image.failed", { detail: reason.slice(0, 500) });
|
|
63
|
-
fail(deps, id, `The picture couldn't be made: ${reason.slice(0, 500)} Check the image
|
|
64
|
+
fail(deps, id, `The picture couldn't be made: ${reason.slice(0, 500)} Check the image service in Settings → Keys & services, then press Make the picture again.`);
|
|
64
65
|
});
|
|
65
66
|
return { ok: true, value: imageRow(deps, id) };
|
|
66
67
|
}
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
// Every install has this channel (migration 0032). A template or project whose channel is
|
|
2
2
|
// NULL, or names a channel this install does not have, belongs to it.
|
|
3
3
|
export const defaultChannelId = "00000000-0000-4000-8000-000000000001";
|
|
4
|
-
export const defaultChannelName = "My channel";
|
|
5
4
|
export const castKinds = ["character", "creature", "place", "object"];
|
|
@@ -8,9 +8,6 @@ import { defaultDocumentTheme } from "../document/model.js";
|
|
|
8
8
|
import { entryByName } from "../library/repo.js";
|
|
9
9
|
import { channelById, resolveChannelId } from "./repo.js";
|
|
10
10
|
import { brandDocument, castSnapshot } from "./runs.js";
|
|
11
|
-
export function channelChanged(db, before, after) {
|
|
12
|
-
return resolveChannelId(db, before.channelId) !== resolveChannelId(db, after.channelId);
|
|
13
|
-
}
|
|
14
11
|
export function rebrandedEdit(db, base, edit) {
|
|
15
12
|
const oldId = resolveChannelId(db, base.channelId);
|
|
16
13
|
const newId = resolveChannelId(db, edit.config.channelId);
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
+
import { channelSocialOf } from "../social/channel.js";
|
|
2
3
|
import { aiDisclosureSettings } from "../studio/disclosure.js";
|
|
3
4
|
import { channelAmbientBedSchema } from "../video/ambient-bed-schema.js";
|
|
4
5
|
import { castKinds, defaultChannelId, } from "./model.js";
|
|
@@ -25,6 +26,8 @@ const channelRow = z.object({
|
|
|
25
26
|
brand_json: z.string(),
|
|
26
27
|
series_brief: z.string(),
|
|
27
28
|
ai_disclosure: z.enum(aiDisclosureSettings),
|
|
29
|
+
x_tier: z.string().optional(),
|
|
30
|
+
social_accounts: z.string().optional(),
|
|
28
31
|
version: z.number(),
|
|
29
32
|
created_at: z.string(),
|
|
30
33
|
updated_at: z.string(),
|
|
@@ -69,6 +72,7 @@ function channelOf(row) {
|
|
|
69
72
|
brand: brandSchema.parse(JSON.parse(value.brand_json)),
|
|
70
73
|
seriesBrief: value.series_brief,
|
|
71
74
|
aiDisclosure: value.ai_disclosure,
|
|
75
|
+
...((social) => ({ xTier: social.xTier, socialAccounts: social.accounts }))(channelSocialOf(value.x_tier, value.social_accounts)),
|
|
72
76
|
version: value.version,
|
|
73
77
|
createdAt: value.created_at,
|
|
74
78
|
updatedAt: value.updated_at,
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { languageSchema } from "../../kernel/ports/languages.js";
|
|
3
|
+
import { socialPlatforms, xTiers } from "../social/model.js";
|
|
3
4
|
import { aiDisclosureSettings } from "../studio/disclosure.js";
|
|
4
5
|
import { ambientBedProblems } from "../video/ambient-bed.js";
|
|
5
6
|
import { channelAmbientBedSchema } from "../video/ambient-bed-schema.js";
|
|
@@ -9,7 +10,7 @@ import { castKinds } from "./model.js";
|
|
|
9
10
|
const id = z.uuid();
|
|
10
11
|
const colour = z
|
|
11
12
|
.string()
|
|
12
|
-
.regex(/^#[0-9a-fA-F]{6}$/, "Use a
|
|
13
|
+
.regex(/^#[0-9a-fA-F]{6}$/, "Use a color like #FFD700.")
|
|
13
14
|
.transform((value) => value.toUpperCase());
|
|
14
15
|
const optionalText = (max) => z.string().trim().max(max).optional();
|
|
15
16
|
const fontId = z
|
|
@@ -75,6 +76,11 @@ export const channelUpdateSchema = z
|
|
|
75
76
|
name,
|
|
76
77
|
brand: brandKitSchema,
|
|
77
78
|
seriesBrief: z.string().max(10000, "Keep the series brief to 10,000 characters or fewer."),
|
|
79
|
+
// Brand → Posting (`slices/social`): left out, they stay as they are.
|
|
80
|
+
xTier: z.enum(xTiers, "Choose Free, Premium or Premium+.").optional(),
|
|
81
|
+
socialAccounts: z
|
|
82
|
+
.partialRecord(z.enum(socialPlatforms), z.string().max(260, "Keep each account name to 260 characters or fewer."))
|
|
83
|
+
.optional(),
|
|
78
84
|
baseVersion: z.number().int().positive(),
|
|
79
85
|
})
|
|
80
86
|
.strict()
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { transact } from "../../kernel/db/tx.js";
|
|
2
|
+
import { accountsProblem, writeChannelSocial } from "../social/channel.js";
|
|
2
3
|
import { defaultChannelId } from "./model.js";
|
|
3
4
|
import { castMemberById, castOfChannel, channelById, channelSummaries } from "./repo.js";
|
|
4
5
|
import { castMemberCreateSchema, castMemberUpdateSchema, castMoveSchema, channelAiDisclosureSchema, channelCreateSchema, channelUpdateSchema, templateChannelSchema, } from "./schema.js";
|
|
@@ -50,9 +51,18 @@ export function updateChannel(deps, id, input) {
|
|
|
50
51
|
if (previous.version !== parsed.data.baseVersion)
|
|
51
52
|
return { ok: false, reason: "conflict" };
|
|
52
53
|
const brand = parsed.data.brand;
|
|
54
|
+
const accountsWrong = parsed.data.socialAccounts === undefined
|
|
55
|
+
? undefined
|
|
56
|
+
: accountsProblem(parsed.data.socialAccounts);
|
|
57
|
+
if (accountsWrong !== undefined)
|
|
58
|
+
return { ok: false, reason: "invalid-input", message: accountsWrong };
|
|
53
59
|
deps.db
|
|
54
60
|
.prepare("UPDATE channels SET name=?,brand_json=?,series_brief=?,version=version+1,updated_at=? WHERE id=? AND version=?")
|
|
55
61
|
.run(parsed.data.name, JSON.stringify(brand), parsed.data.seriesBrief, deps.clock.now().toISOString(), id, parsed.data.baseVersion);
|
|
62
|
+
writeChannelSocial(deps.db, id, {
|
|
63
|
+
xTier: parsed.data.xTier,
|
|
64
|
+
accounts: parsed.data.socialAccounts,
|
|
65
|
+
});
|
|
56
66
|
const saved = channelById(deps.db, id);
|
|
57
67
|
if (saved === undefined)
|
|
58
68
|
throw new Error("The saved channel could not be read back");
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { transact } from "../../kernel/db/tx.js";
|
|
3
|
-
import {
|
|
3
|
+
import { readJsonSetting, writeSetting } from "../settings/repo.js";
|
|
4
4
|
import { channelById } from "./repo.js";
|
|
5
5
|
import { titlesFromCsv, titlesFromLines } from "./studio-csv.js";
|
|
6
6
|
export const videoTitleMax = 500;
|
|
@@ -17,7 +17,7 @@ const importSchema = z
|
|
|
17
17
|
// The preview's "Keep only titles containing…" text, remembered for the next import.
|
|
18
18
|
filter: z
|
|
19
19
|
.string()
|
|
20
|
-
.max(importFilterMax, `The filter is too long to remember. Shorten "Keep only titles containing…" on the channel's
|
|
20
|
+
.max(importFilterMax, `The filter is too long to remember. Shorten "Keep only titles containing…" on the channel's Titles to avoid tab to at most ${importFilterMax} characters.`)
|
|
21
21
|
.optional(),
|
|
22
22
|
})
|
|
23
23
|
.strict();
|
|
@@ -47,7 +47,7 @@ export function previewChannelVideos(deps, channelId, input) {
|
|
|
47
47
|
const read = importOf(input);
|
|
48
48
|
if (!read.ok)
|
|
49
49
|
return read;
|
|
50
|
-
const filter =
|
|
50
|
+
const filter = readJsonSetting(deps.db, filterKey(channelId), z.string(), "");
|
|
51
51
|
return { ok: true, value: { titles: read.value.titles, filter } };
|
|
52
52
|
}
|
|
53
53
|
// Titles already on the channel (any case) and repeats within the import are skipped.
|
|
@@ -69,18 +69,6 @@ export function importChannelVideos(deps, channelId, input) {
|
|
|
69
69
|
return { ok: true, value: { added, skipped: titles.length - added } };
|
|
70
70
|
});
|
|
71
71
|
}
|
|
72
|
-
// Stored as JSON, like every row of the `settings` table a backup carries.
|
|
73
|
-
function storedFilter(stored) {
|
|
74
|
-
if (stored === undefined)
|
|
75
|
-
return "";
|
|
76
|
-
try {
|
|
77
|
-
const value = JSON.parse(stored);
|
|
78
|
-
return typeof value === "string" ? value : "";
|
|
79
|
-
}
|
|
80
|
-
catch {
|
|
81
|
-
return "";
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
72
|
function importOf(input) {
|
|
85
73
|
const parsed = importSchema.safeParse(input);
|
|
86
74
|
if (!parsed.success)
|