@gentbajko/slopify 2.5.0 → 3.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +201 -21
- package/NOTICE +12 -0
- package/README.md +24 -41
- package/SUBTITLES.md +1 -1
- package/dist/adapter-registry.js +31 -0
- package/dist/adapters/alignment/cache.js +4 -1
- package/dist/adapters/alignment/ctc.js +25 -23
- package/dist/adapters/alignment/index.js +37 -6
- package/dist/adapters/alignment/multilingual.js +267 -0
- package/dist/adapters/alignment/protocol.js +11 -0
- package/dist/adapters/alignment/quality.js +4 -3
- package/dist/adapters/alignment/sentences.js +95 -0
- package/dist/adapters/alignment/spec.js +24 -0
- package/dist/adapters/alignment/spell.js +769 -0
- package/dist/adapters/alignment/text.js +56 -9
- package/dist/adapters/alignment/window.js +34 -30
- package/dist/adapters/alignment/worker.js +23 -21
- package/dist/adapters/explain.js +11 -11
- package/dist/adapters/host-cli/index.js +44 -5
- package/dist/adapters/host-cli/transport.js +7 -5
- package/dist/adapters/image/bytes.js +2 -2
- package/dist/adapters/image/codex-output.js +1 -1
- package/dist/adapters/image/codex.js +87 -22
- package/dist/adapters/image/fal.js +13 -5
- package/dist/adapters/image/google.js +11 -8
- package/dist/adapters/image/openai.js +14 -7
- package/dist/adapters/image/reference.js +34 -0
- package/dist/adapters/image/replicate.js +7 -2
- package/dist/adapters/image/video.js +2 -2
- package/dist/adapters/key-probes.js +85 -0
- package/dist/adapters/llm/claude-code.js +168 -32
- package/dist/adapters/llm/cli-login-error.js +1 -1
- package/dist/adapters/llm/codex-limits.js +157 -0
- package/dist/adapters/llm/codex.js +99 -12
- package/dist/adapters/llm/document-workspace.js +2 -2
- package/dist/adapters/llm/gemini-workspace.js +1 -1
- package/dist/adapters/llm/gemini.js +16 -3
- package/dist/adapters/llm/image-workspace.js +58 -0
- package/dist/adapters/llm/openrouter.js +11 -3
- package/dist/adapters/llm/run-cli.js +12 -7
- package/dist/adapters/tts/cartesia.js +10 -0
- package/dist/adapters/tts/elevenlabs.js +31 -9
- package/dist/adapters/tts/gemini.js +206 -0
- package/dist/adapters/tts/inworld-async.js +9 -5
- package/dist/adapters/tts/inworld.js +17 -3
- package/dist/adapters/tts/openai.js +3 -0
- package/dist/adapters/tts/pcm-mp3.js +62 -0
- package/dist/adapters/tts/system.js +147 -0
- package/dist/adapters/tts/voice-languages.js +80 -0
- package/dist/assets/fonts/noto/NotoSans-Regular.ttf +0 -0
- package/dist/assets/fonts/noto/NotoSansArabic-Regular.ttf +0 -0
- package/dist/assets/fonts/noto/NotoSansDevanagari-Regular.ttf +0 -0
- package/dist/assets/fonts/noto/NotoSansHebrew-Regular.ttf +0 -0
- package/dist/assets/fonts/noto/NotoSansThai-Regular.ttf +0 -0
- package/dist/assets/fonts/noto/OFL.txt +93 -0
- package/dist/assets/fonts/noto/SOURCE.txt +18 -0
- package/dist/assets/models.yaml +38 -0
- package/dist/assets/sample/sample-audiobook.tar +0 -0
- package/dist/assets/sample/sample-podcast.tar +0 -0
- package/dist/assets/sample/sample-project.tar +0 -0
- package/dist/assets/style-preview/narration.mp3 +0 -0
- package/dist/assets/style-preview/tall-1.jpg +0 -0
- package/dist/assets/style-preview/tall-2.jpg +0 -0
- package/dist/assets/style-preview/tall-3.jpg +0 -0
- package/dist/assets/style-preview/wide-1.jpg +0 -0
- package/dist/assets/style-preview/wide-2.jpg +0 -0
- package/dist/assets/style-preview/wide-3.jpg +0 -0
- package/dist/catalog/merge.js +132 -0
- package/dist/catalog/registry.js +17 -14
- package/dist/catalog/schema.js +2 -0
- package/dist/catalog/store.js +147 -8
- package/dist/catalog/validate.js +2 -2
- package/dist/compose.yaml +71 -0
- package/dist/edge/autostart/docker-record.js +32 -0
- package/dist/edge/autostart/docker.js +118 -0
- package/dist/edge/autostart/index.js +78 -0
- package/dist/edge/autostart/launcher.js +176 -0
- package/dist/edge/autostart/model.js +3 -0
- package/dist/edge/autostart/native.js +210 -0
- package/dist/edge/autostart/prompt.js +72 -0
- package/dist/edge/autostart/service.js +90 -0
- package/dist/edge/cli-args.js +71 -0
- package/dist/edge/cli.js +64 -63
- package/dist/edge/docker-install/activation.js +56 -0
- package/dist/edge/docker-install/apply.js +458 -0
- package/dist/edge/docker-install/engine.js +362 -0
- package/dist/edge/docker-install/run.js +160 -0
- package/dist/edge/docker-install/state.js +146 -0
- package/dist/edge/{docker-projects → docker-install}/volume.js +1 -5
- package/dist/edge/docker.js +14 -22
- package/dist/edge/events/hub.js +3 -1
- package/dist/edge/host-cli.js +1 -1
- package/dist/edge/http/actions.js +38 -11
- package/dist/edge/http/app.js +60 -2
- package/dist/edge/http/auditions.js +70 -0
- package/dist/edge/http/autostart.js +37 -0
- package/dist/edge/http/byte-range.js +42 -0
- package/dist/edge/http/channel-memory.js +92 -0
- package/dist/edge/http/channels.js +128 -0
- package/dist/edge/http/diagnostics.js +1 -0
- package/dist/edge/http/draft-files.js +2 -4
- package/dist/edge/http/entries.js +22 -3
- package/dist/edge/http/files.js +11 -7
- package/dist/edge/http/folder-location.js +1 -1
- package/dist/edge/http/home.js +39 -0
- package/dist/edge/http/host-cli.js +29 -5
- package/dist/edge/http/narration-peaks.js +76 -0
- package/dist/edge/http/onboarding.js +212 -0
- package/dist/edge/http/open-folder.js +1 -1
- package/dist/edge/http/patch-notes.js +45 -0
- package/dist/edge/http/planning.js +4 -2
- package/dist/edge/http/project-create.js +87 -0
- package/dist/edge/http/project-templates.js +19 -2
- package/dist/edge/http/projects.js +37 -100
- package/dist/edge/http/prompts.js +40 -3
- package/dist/edge/http/pronunciations.js +23 -4
- package/dist/edge/http/providers.js +119 -3
- package/dist/edge/http/reviews.js +131 -0
- package/dist/edge/http/revision-files.js +3 -5
- package/dist/edge/http/revisions.js +1 -1
- package/dist/edge/http/run-cost.js +25 -0
- package/dist/edge/http/schedules.js +115 -6
- package/dist/edge/http/settings.js +84 -6
- package/dist/edge/http/storage-files.js +50 -0
- package/dist/edge/http/storage.js +43 -2
- package/dist/edge/http/studio.js +287 -0
- package/dist/edge/http/style-preview.js +61 -0
- package/dist/edge/http/subtitles.js +1 -1
- package/dist/edge/http/trash.js +69 -0
- package/dist/edge/http/tutorials.js +59 -0
- package/dist/edge/http/update.js +24 -3
- package/dist/edge/http/whats-new.js +15 -0
- package/dist/edge/http/youtube-edits.js +92 -0
- package/dist/edge/native-update.js +62 -0
- package/dist/extension/slopify-studio-chrome.zip +0 -0
- package/dist/extension/slopify-studio-firefox.zip +0 -0
- package/dist/host-cli/gemini-login.js +83 -0
- package/dist/host-cli/open-folder.js +10 -10
- package/dist/host-cli/runtime.js +9 -1
- package/dist/host-cli/status.js +4 -2
- package/dist/kernel/db/migrations/0025-automatic-reviews.sql +40 -0
- package/dist/kernel/db/migrations/0026-schedule-topic-generation.sql +23 -0
- package/dist/kernel/db/migrations/0027-run-cost.sql +58 -0
- package/dist/kernel/db/migrations/0030-automatic-retries.sql +18 -0
- package/dist/kernel/db/migrations/0031-prompt-history-and-description-edits.sql +33 -0
- package/dist/kernel/db/migrations/0032-channels.sql +65 -0
- package/dist/kernel/db/migrations/0034-script-prompts.sql +23 -0
- package/dist/kernel/db/migrations/0035-narration-aliases.sql +13 -0
- package/dist/kernel/db/migrations/0037-project-uploads.sql +8 -0
- package/dist/kernel/db/migrations/0038-voice-languages.sql +7 -0
- package/dist/kernel/db/migrations/0039-channel-essentials.sql +64 -0
- package/dist/kernel/db/migrations/0040-standalone-usage.sql +35 -0
- package/dist/kernel/db/migrations/0041-cast-hosts.sql +3 -0
- package/dist/kernel/paths.js +27 -6
- package/dist/kernel/pipeline.js +4 -1
- package/dist/kernel/ports/host-cli.js +122 -14
- package/dist/kernel/ports/key-probe.js +1 -0
- package/dist/kernel/ports/languages.js +157 -0
- package/dist/kernel/ports/model.js +10 -5
- package/dist/kernel/ports/narration-aliases.js +60 -0
- package/dist/kernel/ports/plan-limits.js +36 -0
- package/dist/kernel/ports/system-speech.js +203 -0
- package/dist/kernel/runner/attempt.js +17 -8
- package/dist/kernel/runner/audition.js +36 -0
- package/dist/kernel/runner/graph.js +40 -5
- package/dist/kernel/runner/index.js +39 -9
- package/dist/kernel/runner/meter.js +1 -0
- package/dist/kernel/runner/providers.js +142 -7
- package/dist/kernel/runner/retry-policy.js +43 -0
- package/dist/kernel/runner/standalone.js +134 -0
- package/dist/kernel/runner/work-authority.js +28 -2
- package/dist/main.js +277 -16
- package/dist/model-catalog.js +2 -0
- package/dist/patch-notes/0.4.2-to-1.0.md +93 -0
- package/dist/patch-notes/1.0-to-2.0.md +74 -0
- package/dist/patch-notes/2.0-to-3.0.md +257 -0
- package/dist/patch-notes/3.0.0.md +70 -0
- package/dist/patch-notes/3.0.1.md +105 -0
- package/dist/patch-notes/index.json +32 -0
- package/dist/slices/admission/model.js +27 -0
- package/dist/slices/admission/repo.js +24 -4
- package/dist/slices/admission/rules.js +80 -2
- package/dist/slices/admission/schema.js +58 -1
- package/dist/slices/admission/short-mode.js +47 -0
- package/dist/slices/admission/start.js +42 -6
- package/dist/slices/article/continuation.js +2 -2
- package/dist/slices/article/provided-entries.js +2 -2
- package/dist/slices/article/run.js +6 -2
- package/dist/slices/article/segments.js +3 -3
- package/dist/slices/backups/folder.js +14 -6
- package/dist/slices/batch/index.js +7 -2
- package/dist/slices/cancel/index.js +1 -1
- package/dist/slices/channels/cast-images.js +110 -0
- package/dist/slices/channels/cast-match.js +32 -0
- package/dist/slices/channels/images.js +19 -0
- package/dist/slices/channels/model.js +5 -0
- package/dist/slices/channels/rebrand.js +100 -0
- package/dist/slices/channels/repo.js +221 -0
- package/dist/slices/channels/runs.js +129 -0
- package/dist/slices/channels/schema.js +152 -0
- package/dist/slices/channels/service.js +180 -0
- package/dist/slices/channels/studio-csv.js +110 -0
- package/dist/slices/channels/videos.js +120 -0
- package/dist/slices/checkpoints/recovery.js +10 -0
- package/dist/slices/control/index.js +5 -2
- package/dist/slices/control/providers.js +4 -1
- package/dist/slices/document/fonts.js +1 -1
- package/dist/slices/document/render.js +1 -1
- package/dist/slices/episodes/related.js +90 -0
- package/dist/slices/episodes/repo.js +60 -0
- package/dist/slices/episodes/service.js +58 -0
- package/dist/slices/episodes/summarize.js +170 -0
- package/dist/slices/estimate/index.js +109 -28
- package/dist/slices/estimate/requests.js +68 -2
- package/dist/slices/eta/history.js +56 -0
- package/dist/slices/eta/model.js +63 -0
- package/dist/slices/eta/view.js +25 -0
- package/dist/slices/fixes/rules.js +73 -0
- package/dist/slices/fonts/coverage.js +82 -0
- package/dist/slices/fonts/sfnt.js +74 -0
- package/dist/slices/images/run.js +8 -5
- package/dist/slices/images/scale.js +84 -0
- package/dist/slices/library/diff.js +122 -0
- package/dist/slices/library/history.js +100 -0
- package/dist/slices/library/model.js +3 -0
- package/dist/slices/library/photorealistic.js +49 -0
- package/dist/slices/library/repo.js +19 -14
- package/dist/slices/library/save.js +62 -12
- package/dist/slices/library/slots.js +7 -1
- package/dist/slices/library/used-by.js +153 -0
- package/dist/slices/loudness/level-pieces.js +41 -0
- package/dist/slices/loudness/loudnorm.js +164 -0
- package/dist/slices/loudness/model.js +153 -0
- package/dist/slices/model-upkeep/model.js +10 -0
- package/dist/slices/model-upkeep/switch.js +217 -0
- package/dist/slices/model-upkeep/usage.js +185 -0
- package/dist/slices/narration/aliases-library.js +31 -0
- package/dist/slices/narration/aliases-schema.js +53 -0
- package/dist/slices/narration/aliases.js +49 -0
- package/dist/slices/narration/blocks.js +352 -0
- package/dist/slices/narration/chunk.js +19 -7
- package/dist/slices/narration/concat.js +2 -2
- package/dist/slices/narration/describe.js +45 -0
- package/dist/slices/narration/pauses-model.js +63 -0
- package/dist/slices/narration/pauses.js +269 -0
- package/dist/slices/narration/peaks-model.js +1 -0
- package/dist/slices/narration/peaks.js +94 -0
- package/dist/slices/narration/preparation.js +21 -3
- package/dist/slices/narration/pronunciation-chunks.js +3 -3
- package/dist/slices/narration/pronunciation.js +72 -25
- package/dist/slices/narration/run.js +6 -6
- package/dist/slices/narration/shared-glossary.js +1 -1
- package/dist/slices/narration/steering.js +15 -4
- package/dist/slices/notifications/notifier.js +47 -3
- package/dist/slices/notifications/rules.js +48 -3
- package/dist/slices/onboarding/copy.js +143 -0
- package/dist/slices/onboarding/first-run.js +72 -0
- package/dist/slices/onboarding/full-video.js +81 -0
- package/dist/slices/onboarding/install.js +119 -0
- package/dist/slices/onboarding/model.js +85 -0
- package/dist/slices/onboarding/packs.js +491 -0
- package/dist/slices/onboarding/quick-short.js +144 -0
- package/dist/slices/onboarding/sample.js +132 -0
- package/dist/slices/onboarding/state.js +68 -0
- package/dist/slices/onboarding/template.js +73 -0
- package/dist/slices/patch-notes/library.js +73 -0
- package/dist/slices/patch-notes/seen.js +77 -0
- package/dist/slices/play-drafts/convert.js +127 -8
- package/dist/slices/play-drafts/readiness.js +4 -1
- package/dist/slices/play-drafts/repo.js +2 -0
- package/dist/slices/play-drafts/review-inputs.js +20 -3
- package/dist/slices/play-drafts/schema.js +89 -0
- package/dist/slices/play-drafts/service.js +4 -1
- package/dist/slices/play-drafts/start.js +43 -18
- package/dist/slices/project-templates/from-project.js +59 -6
- package/dist/slices/project-templates/next-chapter.js +94 -0
- package/dist/slices/project-templates/one-off.js +26 -0
- package/dist/slices/project-templates/repo.js +2 -1
- package/dist/slices/project-templates/schema.js +7 -1
- package/dist/slices/project-templates/service.js +15 -4
- package/dist/slices/project-templates/setup.js +18 -2
- package/dist/slices/rebuild/admission-repo.js +1 -1
- package/dist/slices/rebuild/model.js +6 -0
- package/dist/slices/rebuild/preview-details.js +46 -3
- package/dist/slices/rebuild/preview-plan.js +13 -6
- package/dist/slices/rebuild/recipe-audio-parts.js +40 -7
- package/dist/slices/rebuild/recipe-audio.js +66 -12
- package/dist/slices/rebuild/recipe-build.js +8 -4
- package/dist/slices/rebuild/recipe-cast.js +18 -0
- package/dist/slices/rebuild/recipe-checkpoints.js +11 -9
- package/dist/slices/rebuild/recipe-describe.js +93 -0
- package/dist/slices/rebuild/recipe-edit.js +13 -1
- package/dist/slices/rebuild/recipe-exports.js +67 -8
- package/dist/slices/rebuild/recipe-input-schema.js +35 -0
- package/dist/slices/rebuild/recipe-legacy.js +9 -0
- package/dist/slices/rebuild/recipe-loudness.js +45 -0
- package/dist/slices/rebuild/recipe-model.js +25 -6
- package/dist/slices/rebuild/recipe-pauses.js +7 -0
- package/dist/slices/rebuild/recipe-preparation.js +3 -3
- package/dist/slices/rebuild/recipe-provider-choice.js +11 -1
- package/dist/slices/rebuild/recipe-reference.js +15 -1
- package/dist/slices/rebuild/recipe-reviews.js +117 -0
- package/dist/slices/rebuild/recipe-save.js +4 -1
- package/dist/slices/rebuild/recipe-shorts.js +23 -3
- package/dist/slices/rebuild/recipe-text.js +114 -19
- package/dist/slices/rebuild/recipe-validation.js +14 -2
- package/dist/slices/rebuild/recipe-visual.js +96 -33
- package/dist/slices/rebuild/recipe-voices.js +286 -0
- package/dist/slices/rebuild/recipe-work.js +77 -6
- package/dist/slices/rebuild/recipe-youtube.js +2 -0
- package/dist/slices/rebuild/recovery-conflict.js +7 -3
- package/dist/slices/rebuild/recovery-model.js +3 -0
- package/dist/slices/rebuild/recovery-selection.js +28 -2
- package/dist/slices/rebuild/recovery.js +35 -17
- package/dist/slices/rebuild/repo.js +3 -3
- package/dist/slices/rebuild/retry.js +18 -0
- package/dist/slices/rebuild/review-redo.js +125 -0
- package/dist/slices/rebuild/runtime-actions.js +5 -0
- package/dist/slices/rebuild/runtime-admission.js +4 -1
- package/dist/slices/rebuild/runtime-animate.js +4 -4
- package/dist/slices/rebuild/runtime-article.js +5 -5
- package/dist/slices/rebuild/runtime-document.js +4 -4
- package/dist/slices/rebuild/runtime-export-bed.js +43 -0
- package/dist/slices/rebuild/runtime-export-edit.js +70 -11
- package/dist/slices/rebuild/runtime-export-inputs.js +52 -10
- package/dist/slices/rebuild/runtime-export-short.js +85 -0
- package/dist/slices/rebuild/runtime-export.js +84 -19
- package/dist/slices/rebuild/runtime-figure-card.js +87 -0
- package/dist/slices/rebuild/runtime-image.js +21 -2
- package/dist/slices/rebuild/runtime-local.js +148 -24
- package/dist/slices/rebuild/runtime-materialize.js +8 -0
- package/dist/slices/rebuild/runtime-narration-reuse.js +2 -1
- package/dist/slices/rebuild/runtime-narration-text.js +7 -7
- package/dist/slices/rebuild/runtime-pictures.js +28 -0
- package/dist/slices/rebuild/runtime-piece-label.js +27 -0
- package/dist/slices/rebuild/runtime-provider.js +126 -18
- package/dist/slices/rebuild/runtime-publication.js +21 -5
- package/dist/slices/rebuild/runtime-review.js +267 -0
- package/dist/slices/rebuild/runtime-run.js +17 -11
- package/dist/slices/rebuild/runtime-shorts.js +44 -17
- package/dist/slices/rebuild/runtime-store.js +62 -11
- package/dist/slices/rebuild/runtime-subtitles.js +72 -19
- package/dist/slices/rebuild/runtime-voices.js +103 -0
- package/dist/slices/rebuild/runtime-youtube.js +7 -6
- package/dist/slices/rebuild/service-readiness.js +3 -3
- package/dist/slices/rebuild/soften.js +49 -0
- package/dist/slices/rebuild/transition-repo.js +9 -2
- package/dist/slices/research/run.js +4 -4
- package/dist/slices/research/synthesis.js +2 -2
- package/dist/slices/reviews/model.js +105 -0
- package/dist/slices/reviews/outcome.js +22 -0
- package/dist/slices/reviews/repo.js +118 -0
- package/dist/slices/reviews/rules.js +63 -0
- package/dist/slices/reviews/verdict.js +71 -0
- package/dist/slices/reviews/view.js +30 -0
- package/dist/slices/revisions/adopt-content.js +5 -2
- package/dist/slices/revisions/adopt.js +3 -1
- package/dist/slices/revisions/downloads.js +7 -1
- package/dist/slices/revisions/image-plan.js +183 -0
- package/dist/slices/revisions/mutation-assets.js +10 -0
- package/dist/slices/revisions/mutation-cues.js +71 -0
- package/dist/slices/revisions/mutation-request.js +2 -1
- package/dist/slices/revisions/mutations.js +97 -3
- package/dist/slices/revisions/schema.js +11 -1
- package/dist/slices/run-cost/limits.js +128 -0
- package/dist/slices/run-cost/meter.js +47 -0
- package/dist/slices/run-cost/panel.js +256 -0
- package/dist/slices/run-cost/pricing.js +95 -0
- package/dist/slices/run-cost/week.js +108 -0
- package/dist/slices/schedules/agenda.js +222 -0
- package/dist/slices/schedules/repo.js +58 -18
- package/dist/slices/schedules/scheduler.js +68 -17
- package/dist/slices/schedules/schema.js +179 -1
- package/dist/slices/schedules/service.js +43 -4
- package/dist/slices/schedules/similar.js +128 -0
- package/dist/slices/schedules/topic-list.js +163 -0
- package/dist/slices/schedules/topics.js +523 -0
- package/dist/slices/settings/first-run.js +106 -0
- package/dist/slices/settings/health.js +247 -0
- package/dist/slices/settings/key-guides.js +138 -0
- package/dist/slices/settings/key-test.js +93 -0
- package/dist/slices/settings/keys.js +10 -5
- package/dist/slices/settings/model-reach.js +52 -0
- package/dist/slices/settings/model.js +55 -1
- package/dist/slices/settings/models.js +5 -1
- package/dist/slices/settings/playback.js +15 -0
- package/dist/slices/settings/readiness.js +14 -2
- package/dist/slices/settings/repo.js +17 -3
- package/dist/slices/settings/tutorial-schema.js +5 -0
- package/dist/slices/settings/voices.js +64 -4
- package/dist/slices/settings/whats-new.js +64 -0
- package/dist/slices/shorts/captions.js +9 -0
- package/dist/slices/shorts/pick.js +3 -3
- package/dist/slices/shorts/prompts.js +1 -1
- package/dist/slices/shorts/render.js +63 -13
- package/dist/slices/storage/asset-name.js +3 -0
- package/dist/slices/storage/backup-export.js +48 -9
- package/dist/slices/storage/backup-format.js +38 -1
- package/dist/slices/storage/backup-import.js +191 -19
- package/dist/slices/storage/delete-project.js +7 -4
- package/dist/slices/storage/documents.js +87 -0
- package/dist/slices/storage/downloads.js +33 -1
- package/dist/slices/storage/files-location.js +531 -0
- package/dist/slices/storage/layout.js +14 -4
- package/dist/slices/storage/model.js +11 -0
- package/dist/slices/storage/portable.js +94 -12
- package/dist/slices/storage/reconcile.js +14 -1
- package/dist/slices/storage/repo.js +4 -1
- package/dist/slices/storage/schema.js +11 -0
- package/dist/slices/storage/trim.js +146 -0
- package/dist/slices/studio/disclosure.js +74 -0
- package/dist/slices/studio/model.js +30 -0
- package/dist/slices/studio/pack.js +211 -0
- package/dist/slices/studio/queue.js +69 -0
- package/dist/slices/studio/settings.js +170 -0
- package/dist/slices/style-preview/images.js +61 -0
- package/dist/slices/style-preview/narration.js +33 -0
- package/dist/slices/style-preview/render.js +148 -0
- package/dist/slices/style-preview/schema.js +59 -0
- package/dist/slices/style-preview/service.js +92 -0
- package/dist/slices/style-preview/settings.js +139 -0
- package/dist/slices/subtitles/captions.js +42 -9
- package/dist/slices/subtitles/model.js +22 -0
- package/dist/slices/subtitles/prepare.js +27 -16
- package/dist/slices/subtitles/transcript.js +1 -1
- package/dist/slices/thumbnail/run.js +7 -7
- package/dist/slices/trash/model.js +25 -0
- package/dist/slices/trash/service.js +273 -0
- package/dist/slices/tutorials/anchors.js +57 -0
- package/dist/slices/tutorials/library.js +185 -0
- package/dist/slices/uploads/repo.js +20 -0
- package/dist/slices/video/ambient-bed-schema.js +15 -0
- package/dist/slices/video/ambient-bed.js +95 -0
- package/dist/slices/video/ambient-mix.js +72 -0
- package/dist/slices/video/audio-inputs.js +2 -2
- package/dist/slices/video/cards.js +5 -2
- package/dist/slices/video/edit-list.js +23 -2
- package/dist/slices/video/edit-settings.js +13 -3
- package/dist/slices/video/ffmpeg.js +63 -9
- package/dist/slices/video/figure-card.js +492 -0
- package/dist/slices/video/figure-spans.js +51 -0
- package/dist/slices/video/plan.js +125 -13
- package/dist/slices/video/run.js +14 -12
- package/dist/slices/video/slideshow.js +39 -4
- package/dist/slices/voices/attribution.js +69 -0
- package/dist/slices/voices/audio-files.js +87 -0
- package/dist/slices/voices/audition.js +25 -0
- package/dist/slices/voices/cast.js +74 -0
- package/dist/slices/voices/grouping.js +59 -0
- package/dist/slices/voices/join.js +35 -0
- package/dist/slices/voices/languages.js +25 -0
- package/dist/slices/voices/model.js +218 -0
- package/dist/slices/voices/palette.js +26 -0
- package/dist/slices/voices/panel.js +91 -0
- package/dist/slices/voices/portraits.js +55 -0
- package/dist/slices/voices/script.js +136 -0
- package/dist/slices/voices/timing.js +58 -0
- package/dist/slices/youtube/answer.js +1 -1
- package/dist/slices/youtube/chapters.js +126 -0
- package/dist/slices/youtube/edits-repo.js +84 -0
- package/dist/slices/youtube/edits.js +89 -0
- package/dist/slices/youtube/placeholders.js +98 -0
- package/dist/slices/youtube/transcript.js +11 -4
- package/dist/tutorials/AI-CLIs.md +146 -0
- package/dist/tutorials/Backups.md +121 -0
- package/dist/tutorials/Calendar.md +103 -0
- package/dist/tutorials/Cast-Library.md +112 -0
- package/dist/tutorials/Channels.md +148 -0
- package/dist/tutorials/Costs-and-Run-Cost.md +136 -0
- package/dist/tutorials/Docker.md +171 -0
- package/dist/tutorials/Document-Themes.md +207 -0
- package/dist/tutorials/Editing-a-Project.md +204 -0
- package/dist/tutorials/FAQ.md +134 -0
- package/dist/tutorials/First-Launch-and-Welcome.md +153 -0
- package/dist/tutorials/Home-and-Projects.md +174 -0
- package/dist/tutorials/Home.md +119 -0
- package/dist/tutorials/Install.md +154 -0
- package/dist/tutorials/Intros-and-Outros.md +86 -0
- package/dist/tutorials/Keyboard-Shortcuts-and-Command-Palette.md +215 -0
- package/dist/tutorials/Library-Overview.md +75 -0
- package/dist/tutorials/Models.md +133 -0
- package/dist/tutorials/Multiple-Voices.md +161 -0
- package/dist/tutorials/Narration-Aliases-and-Glossary.md +129 -0
- package/dist/tutorials/Notifications.md +86 -0
- package/dist/tutorials/Other-Languages.md +110 -0
- package/dist/tutorials/PDF-Documents.md +100 -0
- package/dist/tutorials/Play-Images.md +135 -0
- package/dist/tutorials/Play-Narration.md +167 -0
- package/dist/tutorials/Play-Outputs.md +109 -0
- package/dist/tutorials/Play-Overview.md +216 -0
- package/dist/tutorials/Play-Title-and-Article.md +135 -0
- package/dist/tutorials/Play-Video-and-Style.md +143 -0
- package/dist/tutorials/Project-Page.md +228 -0
- package/dist/tutorials/Prompts.md +136 -0
- package/dist/tutorials/Providers-and-Keys.md +290 -0
- package/dist/tutorials/Publishing-to-YouTube.md +135 -0
- package/dist/tutorials/Recovery-and-Retries.md +141 -0
- package/dist/tutorials/Reviews-and-Checkpoints.md +143 -0
- package/dist/tutorials/Schedules.md +154 -0
- package/dist/tutorials/Settings-Reference.md +258 -0
- package/dist/tutorials/Shorts.md +116 -0
- package/dist/tutorials/Start-at-Login.md +126 -0
- package/dist/tutorials/Studio-Extension.md +146 -0
- package/dist/tutorials/Templates.md +146 -0
- package/dist/tutorials/Trash.md +87 -0
- package/dist/tutorials/Troubleshooting.md +149 -0
- package/dist/tutorials/Uninstalling-and-Moving.md +113 -0
- package/dist/tutorials/Updating-and-Patch-Notes.md +120 -0
- package/dist/tutorials/Video-Editing.md +135 -0
- package/dist/tutorials/Where-Your-Files-Live.md +149 -0
- package/dist/tutorials/YouTube-Description.md +132 -0
- package/dist/tutorials/Your-First-Short.md +125 -0
- package/dist/tutorials/_Footer.md +1 -0
- package/dist/tutorials/_Sidebar.md +69 -0
- package/dist/updater/service.js +66 -21
- package/dist/web/assets/index-DBLSfqA-.css +1 -0
- package/dist/web/assets/index-DFdIhmm0.js +307 -0
- package/dist/web/assets/jetbrains-mono-cyrillic-400-normal-BEIGL1Tu.woff2 +0 -0
- package/dist/web/assets/jetbrains-mono-cyrillic-400-normal-ugxPyKxw.woff +0 -0
- package/dist/web/assets/jetbrains-mono-greek-400-normal-B9oWc5Lo.woff +0 -0
- package/dist/web/assets/jetbrains-mono-greek-400-normal-C190GLew.woff2 +0 -0
- package/dist/web/assets/jetbrains-mono-latin-400-normal-6-qcROiO.woff +0 -0
- package/dist/web/assets/jetbrains-mono-latin-400-normal-V6pRDFza.woff2 +0 -0
- package/dist/web/assets/jetbrains-mono-latin-ext-400-normal-Bc8Ftmh3.woff2 +0 -0
- package/dist/web/assets/jetbrains-mono-latin-ext-400-normal-fXTG6kC5.woff +0 -0
- package/dist/web/assets/jetbrains-mono-vietnamese-400-normal-CqNFfHCs.woff +0 -0
- package/dist/web/assets/{pdf-BLF2i8c2.js → pdf-B9OY1ioX.js} +1 -1
- package/dist/web/index.html +2 -2
- package/package.json +5 -5
- package/dist/edge/docker-launch.js +0 -81
- package/dist/edge/docker-projects/activation.js +0 -35
- package/dist/edge/docker-projects/claims.js +0 -85
- package/dist/edge/docker-projects/committed.js +0 -40
- package/dist/edge/docker-projects/engine.js +0 -461
- package/dist/edge/docker-projects/install.js +0 -254
- package/dist/edge/docker-projects/prune.js +0 -66
- package/dist/edge/docker-projects/recover.js +0 -100
- package/dist/edge/docker-projects/state.js +0 -225
- package/dist/web/assets/index-CktpxlJd.js +0 -171
- package/dist/web/assets/index-DB-vOnGU.css +0 -1
- package/scripts/docker-run.sh +0 -3
- /package/dist/edge/{docker-projects → docker-install}/tree.js +0 -0
package/dist/catalog/validate.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { usesNarrationPreparation, usesReference, usesShorts, usesYoutubeDescription, } from "../slices/admission/rules.js";
|
|
2
|
-
import { isLocalCliProvider } from "../slices/settings/model.js";
|
|
2
|
+
import { isLocalCliProvider, isUncataloguedProvider } from "../slices/settings/model.js";
|
|
3
3
|
import { usesAnimation } from "../slices/video/edit-settings.js";
|
|
4
4
|
import { referenceRefusal, takesReferenceImage, videoModelsOf } from "./schema.js";
|
|
5
5
|
export function modelFields(draft, catalogue) {
|
|
@@ -32,7 +32,7 @@ export function modelFields(draft, catalogue) {
|
|
|
32
32
|
},
|
|
33
33
|
];
|
|
34
34
|
for (const { field, family, choice, needed } of checks) {
|
|
35
|
-
if (!needed || !choice ||
|
|
35
|
+
if (!needed || !choice || isUncataloguedProvider(choice.provider))
|
|
36
36
|
continue;
|
|
37
37
|
const model = catalogue.models(choice.provider, family).find((m) => m.id === choice.model);
|
|
38
38
|
if (!model)
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
# Slopify on Docker: one service, one private data volume, one host folder for project files.
|
|
2
|
+
#
|
|
3
|
+
# `npx @gentbajko/slopify --docker` copies this file to ~/.local/share/slopify/docker/<name>/
|
|
4
|
+
# and writes the .env beside it, then runs `docker compose up`. You can also run it by hand; see
|
|
5
|
+
# docs/docker.md for the variables. The data volume is external on purpose: `docker compose down`
|
|
6
|
+
# (even with -v) never deletes it.
|
|
7
|
+
name: ${SLOPIFY_NAME:-slopify}
|
|
8
|
+
|
|
9
|
+
services:
|
|
10
|
+
slopify:
|
|
11
|
+
image: ${SLOPIFY_IMAGE:-ghcr.io/gentbajko/slopify:latest}
|
|
12
|
+
container_name: ${SLOPIFY_NAME:-slopify}
|
|
13
|
+
# Your own user id, so project files on the host belong to you (0:0 under rootless Docker).
|
|
14
|
+
user: ${SLOPIFY_USER:-1000:1000}
|
|
15
|
+
restart: unless-stopped
|
|
16
|
+
# Localhost only: anyone who reaches this port controls the app and its provider keys.
|
|
17
|
+
# An empty SLOPIFY_PORT picks a free port.
|
|
18
|
+
ports:
|
|
19
|
+
- 127.0.0.1:${SLOPIFY_PORT-6969}:6969
|
|
20
|
+
volumes:
|
|
21
|
+
- type: volume
|
|
22
|
+
source: data
|
|
23
|
+
target: /data
|
|
24
|
+
- type: bind
|
|
25
|
+
source: ${SLOPIFY_PROJECTS_DIR:?set SLOPIFY_PROJECTS_DIR to the folder for your project files}
|
|
26
|
+
target: /data/projects
|
|
27
|
+
# Scheduled backups' default folder, beside Projects (new installs from 3.0). Without
|
|
28
|
+
# SLOPIFY_BACKUPS_DIR it is an empty folder nobody reads and backups stay in
|
|
29
|
+
# Projects/Backups.
|
|
30
|
+
- type: bind
|
|
31
|
+
source: ${SLOPIFY_BACKUPS_DIR:-./backups-off}
|
|
32
|
+
target: /data/backups
|
|
33
|
+
bind:
|
|
34
|
+
create_host_path: true
|
|
35
|
+
# The host CLI bridge: a folder holding the helper's socket and token, nothing else.
|
|
36
|
+
# With the bridge off it is an empty folder and Settings shows the CLIs as unavailable.
|
|
37
|
+
- type: bind
|
|
38
|
+
source: ${SLOPIFY_HOST_CLI_SHARE:-./host-cli-off}
|
|
39
|
+
target: /opt/slopify-host
|
|
40
|
+
read_only: true
|
|
41
|
+
bind:
|
|
42
|
+
create_host_path: true
|
|
43
|
+
# The update handshake: activation.json says when the installer has committed this version.
|
|
44
|
+
- type: bind
|
|
45
|
+
source: ${SLOPIFY_ACTIVATION_DIR:-./activation}
|
|
46
|
+
target: /opt/slopify-install
|
|
47
|
+
read_only: true
|
|
48
|
+
bind:
|
|
49
|
+
create_host_path: true
|
|
50
|
+
environment:
|
|
51
|
+
SLOPIFY_DOCKER_PROJECTS_DIR: ${SLOPIFY_PROJECTS_DIR}
|
|
52
|
+
SLOPIFY_DOCKER_BACKUPS_DIR: ${SLOPIFY_BACKUPS_DIR:-}
|
|
53
|
+
SLOPIFY_HOST_CLI_DIR: /opt/slopify-host
|
|
54
|
+
SLOPIFY_DOCKER_INSTALL_STATE: /opt/slopify-install/activation.json
|
|
55
|
+
SLOPIFY_UPDATE_TOKEN: ${SLOPIFY_UPDATE_TOKEN:-}
|
|
56
|
+
SLOPIFY_UPDATE_PENDING: ${SLOPIFY_UPDATE_PENDING:-0}
|
|
57
|
+
healthcheck:
|
|
58
|
+
test:
|
|
59
|
+
- CMD
|
|
60
|
+
- node
|
|
61
|
+
- -e
|
|
62
|
+
- "fetch('http://127.0.0.1:6969/api/health').then(r => process.exit(r.ok ? 0 : 1)).catch(() => process.exit(1))"
|
|
63
|
+
interval: 30s
|
|
64
|
+
timeout: 5s
|
|
65
|
+
start_period: 120s
|
|
66
|
+
start_interval: 2s
|
|
67
|
+
|
|
68
|
+
volumes:
|
|
69
|
+
data:
|
|
70
|
+
external: true
|
|
71
|
+
name: ${SLOPIFY_VOLUME:-slopify-data}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { join } from "node:path";
|
|
2
|
+
import { installSchema, readState, writeState } from "../docker-install/state.js";
|
|
3
|
+
import { inspectDockerStart, loginStartFile, loginStartReport, loginStartSchema, } from "./docker.js";
|
|
4
|
+
import { autostartQuestion } from "./prompt.js";
|
|
5
|
+
/**
|
|
6
|
+
* The --docker installer's part: ask "Start Slopify when you log in?" once (or take
|
|
7
|
+
* --autostart / --no-autostart), look whether Docker starts by itself, and leave the answer in
|
|
8
|
+
* the installation's activation folder, which the container reads at
|
|
9
|
+
* /opt/slopify-install/login-start.json. Docker's own settings are only read, never changed.
|
|
10
|
+
*/
|
|
11
|
+
export async function recordLoginStart(o) {
|
|
12
|
+
const install = await readState(join(o.directory, "install.json"), installSchema, o.uid);
|
|
13
|
+
// Rootless Docker runs the container as 0:0 (see compose.yaml's SLOPIFY_USER).
|
|
14
|
+
const manager = install?.user === "0:0" ? "rootless" : "system";
|
|
15
|
+
const path = join(o.directory, "activation", loginStartFile);
|
|
16
|
+
const previous = await readState(path, loginStartSchema, o.uid).catch(() => null);
|
|
17
|
+
let wanted = o.flag ?? previous?.wanted ?? null;
|
|
18
|
+
if (wanted === null && o.interactive)
|
|
19
|
+
wanted = (await o.ask(autostartQuestion)) ?? null;
|
|
20
|
+
const record = {
|
|
21
|
+
version: 1,
|
|
22
|
+
checkedAt: o.now().toISOString(),
|
|
23
|
+
manager,
|
|
24
|
+
wanted,
|
|
25
|
+
...(await inspectDockerStart({ exec: o.exec, manager, host: o.host })),
|
|
26
|
+
};
|
|
27
|
+
await writeState(path, record);
|
|
28
|
+
o.report(wanted === false
|
|
29
|
+
? `Slopify in Docker still starts whenever Docker does, because its container restarts with Docker. When you don't want it running: docker stop ${o.name}`
|
|
30
|
+
: loginStartReport(record));
|
|
31
|
+
return record;
|
|
32
|
+
}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import { posix, win32 } from "node:path";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
// Slopify in Docker restarts with Docker (`restart: unless-stopped` in compose.yaml), so it
|
|
4
|
+
// starts at login exactly when Docker does. Slopify never changes Docker's own setting; the
|
|
5
|
+
// installer looks once, on this machine, and leaves the answer where the container can read
|
|
6
|
+
// it (the read-only /opt/slopify-install folder), because a container can't see its host.
|
|
7
|
+
export const loginStartFile = "login-start.json";
|
|
8
|
+
export const containerLoginStart = `/opt/slopify-install/${loginStartFile}`;
|
|
9
|
+
export const loginStartSchema = z
|
|
10
|
+
.object({
|
|
11
|
+
version: z.literal(1),
|
|
12
|
+
checkedAt: z.string().max(40),
|
|
13
|
+
// yes: Docker starts by itself; no: it doesn't; unknown: this machine couldn't say.
|
|
14
|
+
docker: z.enum(["yes", "no", "unknown"]),
|
|
15
|
+
// Which Docker the installation uses: the system one, or rootless Docker in your account.
|
|
16
|
+
manager: z.enum(["system", "rootless"]),
|
|
17
|
+
// The installer's "Start Slopify when you log in?" answer, or null when it wasn't asked.
|
|
18
|
+
wanted: z.boolean().nullable(),
|
|
19
|
+
// The system the installer ran on, and whether its Docker is Docker Desktop, which has its
|
|
20
|
+
// own sign-in setting instead of a systemd service. Absent in records from before 3.0.1.
|
|
21
|
+
platform: z.enum(["linux", "darwin", "win32", "other"]).optional(),
|
|
22
|
+
desktop: z.boolean().optional(),
|
|
23
|
+
})
|
|
24
|
+
.strict();
|
|
25
|
+
export function dockerHowTo(manager) {
|
|
26
|
+
return manager === "rootless"
|
|
27
|
+
? "Turn on Docker's start at login in a terminal: systemctl --user enable docker (rootless Docker). Slopify then starts with it."
|
|
28
|
+
: "Turn on Docker's start with the computer in a terminal: sudo systemctl enable docker. Slopify then starts with it.";
|
|
29
|
+
}
|
|
30
|
+
export const dockerDesktopHowTo = "Turn on Docker Desktop → Settings → General → Start Docker Desktop when you sign in to your computer. Slopify then starts with it.";
|
|
31
|
+
// Where to turn Docker's start at login on, for the Docker this installation uses. A record
|
|
32
|
+
// from before the installer noted the system can't tell Docker Desktop from Docker Engine
|
|
33
|
+
// when systemctl didn't answer (macOS and Windows have none), so it names both.
|
|
34
|
+
export function howToFor(record) {
|
|
35
|
+
if (record.desktop === true)
|
|
36
|
+
return dockerDesktopHowTo;
|
|
37
|
+
if (record.platform === undefined && record.docker === "unknown")
|
|
38
|
+
return desktopHowTo;
|
|
39
|
+
return dockerHowTo(record.manager);
|
|
40
|
+
}
|
|
41
|
+
export const desktopHowTo = "With Docker Desktop: Docker Desktop → Settings → General → Start Docker Desktop when you sign in. With Docker Engine on Linux: sudo systemctl enable docker. Slopify starts whenever Docker does.";
|
|
42
|
+
/**
|
|
43
|
+
* Reads, never changes, whether Docker starts by itself. `systemctl is-enabled` only reports;
|
|
44
|
+
* it answers "enabled" or "disabled" (exit code 1), and fails without systemd.
|
|
45
|
+
*/
|
|
46
|
+
export async function checkDockerStart(exec, manager) {
|
|
47
|
+
const result = await exec("systemctl", manager === "rootless"
|
|
48
|
+
? ["--user", "is-enabled", "docker.service"]
|
|
49
|
+
: ["is-enabled", "docker.service"]);
|
|
50
|
+
const answer = result.stdout.trim().split("\n")[0]?.trim() ?? "";
|
|
51
|
+
if (["enabled", "enabled-runtime", "alias"].includes(answer))
|
|
52
|
+
return "yes";
|
|
53
|
+
if (["disabled", "masked", "masked-runtime"].includes(answer))
|
|
54
|
+
return "no";
|
|
55
|
+
return "unknown";
|
|
56
|
+
}
|
|
57
|
+
// Docker Desktop's settings files, newest name first: settings-store.json in current
|
|
58
|
+
// versions, settings.json before it.
|
|
59
|
+
export function desktopSettingsPaths(host) {
|
|
60
|
+
const names = ["settings-store.json", "settings.json"];
|
|
61
|
+
if (host.platform === "win32") {
|
|
62
|
+
const appData = host.env.APPDATA?.trim() || win32.join(host.home, "AppData", "Roaming");
|
|
63
|
+
return names.map((name) => win32.join(appData, "Docker", name));
|
|
64
|
+
}
|
|
65
|
+
if (host.platform === "darwin")
|
|
66
|
+
return names.map((name) => posix.join(host.home, "Library", "Group Containers", "group.com.docker", name));
|
|
67
|
+
return names.map((name) => posix.join(host.home, ".docker", "desktop", name));
|
|
68
|
+
}
|
|
69
|
+
// `AutoStart` in settings-store.json, `autoStart` in the older settings.json: Start Docker
|
|
70
|
+
// Desktop when you sign in to your computer.
|
|
71
|
+
export function desktopAutoStart(text) {
|
|
72
|
+
try {
|
|
73
|
+
const value = JSON.parse(text);
|
|
74
|
+
if (typeof value !== "object" || value === null)
|
|
75
|
+
return "unknown";
|
|
76
|
+
const record = value;
|
|
77
|
+
const start = record.AutoStart ?? record.autoStart;
|
|
78
|
+
return start === true ? "yes" : start === false ? "no" : "unknown";
|
|
79
|
+
}
|
|
80
|
+
catch {
|
|
81
|
+
return "unknown";
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Reads, never changes, whether Docker starts by itself on this machine. macOS and Windows run
|
|
86
|
+
* Docker Desktop, whose settings file says; on Linux, Docker Desktop's settings file when it
|
|
87
|
+
* is there, otherwise the system or rootless docker service through systemctl.
|
|
88
|
+
*/
|
|
89
|
+
export async function inspectDockerStart(o) {
|
|
90
|
+
const platform = o.host.platform === "linux" || o.host.platform === "darwin" || o.host.platform === "win32"
|
|
91
|
+
? o.host.platform
|
|
92
|
+
: "other";
|
|
93
|
+
for (const path of desktopSettingsPaths(o.host)) {
|
|
94
|
+
const text = await o.host.read(path);
|
|
95
|
+
if (text !== undefined)
|
|
96
|
+
return { docker: desktopAutoStart(text), desktop: true, platform };
|
|
97
|
+
}
|
|
98
|
+
if (platform === "darwin" || platform === "win32")
|
|
99
|
+
return { docker: "unknown", desktop: true, platform };
|
|
100
|
+
return { docker: await checkDockerStart(o.exec, o.manager), desktop: false, platform };
|
|
101
|
+
}
|
|
102
|
+
// The terminal line the installer prints after its question.
|
|
103
|
+
export function loginStartReport(record) {
|
|
104
|
+
if (record.desktop === true) {
|
|
105
|
+
if (record.docker === "yes")
|
|
106
|
+
return "Slopify starts when you sign in: Docker Desktop starts at sign-in and restarts Slopify.";
|
|
107
|
+
if (record.docker === "no")
|
|
108
|
+
return `Docker Desktop doesn't start when you sign in, so neither does Slopify. ${dockerDesktopHowTo}`;
|
|
109
|
+
return `Slopify couldn't tell whether Docker Desktop starts when you sign in (its settings file didn't say). ${dockerDesktopHowTo}`;
|
|
110
|
+
}
|
|
111
|
+
if (record.docker === "yes")
|
|
112
|
+
return record.manager === "rootless"
|
|
113
|
+
? "Slopify starts when you log in: rootless Docker starts with your login and restarts Slopify."
|
|
114
|
+
: "Slopify starts with this computer: Docker starts at boot and restarts Slopify.";
|
|
115
|
+
if (record.docker === "no")
|
|
116
|
+
return `Docker doesn't start by itself on this computer, so neither does Slopify. ${dockerHowTo(record.manager)}`;
|
|
117
|
+
return `Slopify couldn't tell whether Docker starts by itself here (systemctl didn't answer). ${dockerHowTo(record.manager)}`;
|
|
118
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { access, realpath } from "node:fs/promises";
|
|
2
|
+
import { homedir } from "node:os";
|
|
3
|
+
import { dirname, join, sep } from "node:path";
|
|
4
|
+
import { fileURLToPath } from "node:url";
|
|
5
|
+
import { readState } from "../docker-install/state.js";
|
|
6
|
+
import { isWsl } from "../open-browser.js";
|
|
7
|
+
import { containerLoginStart, loginStartSchema } from "./docker.js";
|
|
8
|
+
import { createNativeAutostart, nodeAutostartExec, nodeAutostartFs } from "./native.js";
|
|
9
|
+
import { createAutostartService } from "./service.js";
|
|
10
|
+
/** The switch for this process: the container's report in Docker, the login entry otherwise. */
|
|
11
|
+
export async function createAutostart(context) {
|
|
12
|
+
if (context.env.SLOPIFY_CONTAINER === "1")
|
|
13
|
+
return createAutostartService(context.db, {
|
|
14
|
+
kind: "docker",
|
|
15
|
+
record: () => context.env.SLOPIFY_DOCKER_INSTALL_STATE === undefined
|
|
16
|
+
? Promise.resolve(null)
|
|
17
|
+
: readLoginStart(containerLoginStart),
|
|
18
|
+
});
|
|
19
|
+
return createAutostartService(context.db, {
|
|
20
|
+
kind: "native",
|
|
21
|
+
native: createNativeAutostart({
|
|
22
|
+
platform: process.platform,
|
|
23
|
+
home: homedir(),
|
|
24
|
+
env: context.env,
|
|
25
|
+
wsl: isWsl(process.platform, context.env),
|
|
26
|
+
fs: nodeAutostartFs,
|
|
27
|
+
exec: nodeAutostartExec,
|
|
28
|
+
node: process.execPath,
|
|
29
|
+
entry: await stableEntry(),
|
|
30
|
+
npxCli: await npxCli(),
|
|
31
|
+
version: context.version,
|
|
32
|
+
dataDir: context.dataDir,
|
|
33
|
+
port: context.port,
|
|
34
|
+
host: context.host,
|
|
35
|
+
log: join(context.logs, "autostart.log"),
|
|
36
|
+
}),
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
export async function readLoginStart(path) {
|
|
40
|
+
try {
|
|
41
|
+
return await readState(path, loginStartSchema, process.getuid?.() ?? -1);
|
|
42
|
+
}
|
|
43
|
+
catch {
|
|
44
|
+
// Damaged, or not the installer's: the same as not knowing.
|
|
45
|
+
return null;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
// This package's cli.js, unless it lives in the npx cache, which npx may clean at any time.
|
|
49
|
+
async function stableEntry() {
|
|
50
|
+
const entry = fileURLToPath(new URL("../cli.js", import.meta.url));
|
|
51
|
+
try {
|
|
52
|
+
await access(entry);
|
|
53
|
+
const real = await realpath(entry);
|
|
54
|
+
return real.includes(`${sep}_npx${sep}`) ? null : real;
|
|
55
|
+
}
|
|
56
|
+
catch {
|
|
57
|
+
// Running from source (tsx): there is no built entry to start.
|
|
58
|
+
return null;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
// npm's npx script beside this Node (the Windows and the Unix installation layouts).
|
|
62
|
+
async function npxCli() {
|
|
63
|
+
const nodeDir = dirname(process.execPath);
|
|
64
|
+
for (const candidate of [
|
|
65
|
+
join(nodeDir, "node_modules", "npm", "bin", "npx-cli.js"),
|
|
66
|
+
join(nodeDir, "..", "lib", "node_modules", "npm", "bin", "npx-cli.js"),
|
|
67
|
+
]) {
|
|
68
|
+
try {
|
|
69
|
+
await access(candidate);
|
|
70
|
+
// Not resolved: Homebrew's links stay put while the folders they point to change.
|
|
71
|
+
return candidate;
|
|
72
|
+
}
|
|
73
|
+
catch {
|
|
74
|
+
// Try the next layout.
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
return null;
|
|
78
|
+
}
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
// The files "Start Slopify when I log in" writes, as pure text. Nothing here touches the disk,
|
|
2
|
+
// so every platform's output is tested on any machine (`launcher.test.ts`).
|
|
3
|
+
//
|
|
4
|
+
// The login entry never names the npx cache: npx may clean it or move it on its next run. It
|
|
5
|
+
// runs a small launcher kept in the data folder instead, which starts, in order:
|
|
6
|
+
// 1. the installed Slopify entry, when Slopify runs from a real install (npm i -g, a local
|
|
7
|
+
// checkout), not from the npx cache;
|
|
8
|
+
// 2. npx's own script next to this Node, asking for the version that was running when the
|
|
9
|
+
// switch was turned on (`@gentbajko/slopify@<version>`), so a login works offline once
|
|
10
|
+
// that version is in the cache and never jumps to a new release by itself;
|
|
11
|
+
// 3. plain `npx` from PATH, the same way.
|
|
12
|
+
// In-app updates still apply: the started version forwards to the newest one installed in the
|
|
13
|
+
// data folder (`updater/forward.ts`). Each start with the switch on writes the launcher again,
|
|
14
|
+
// so it follows the Node, the entry and the version actually in use.
|
|
15
|
+
export const packageName = "@gentbajko/slopify";
|
|
16
|
+
export const launcherMarker = "Written by Slopify: Settings";
|
|
17
|
+
export const desktopMarker = "X-Slopify-Autostart=true";
|
|
18
|
+
export const agentLabel = "stream.slopify.app";
|
|
19
|
+
export const runKey = "HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Run";
|
|
20
|
+
export const runValue = "Slopify";
|
|
21
|
+
// The flags a login start runs with: quiet (no browser tab), and the data folder, port and
|
|
22
|
+
// host of the Slopify that turned the switch on, since no terminal is there to pass them.
|
|
23
|
+
export function launchArgs(spec) {
|
|
24
|
+
return [
|
|
25
|
+
"--no-open",
|
|
26
|
+
"--data-dir",
|
|
27
|
+
spec.dataDir,
|
|
28
|
+
"--port",
|
|
29
|
+
String(spec.port),
|
|
30
|
+
...(spec.host === "127.0.0.1" ? [] : ["--host", spec.host]),
|
|
31
|
+
];
|
|
32
|
+
}
|
|
33
|
+
// POSIX sh single quotes: nothing inside is special except the quote itself.
|
|
34
|
+
export function shQuote(value) {
|
|
35
|
+
return `'${value.replaceAll("'", `'\\''`)}'`;
|
|
36
|
+
}
|
|
37
|
+
export function shellLauncher(spec) {
|
|
38
|
+
const args = launchArgs(spec).map(shQuote).join(" ");
|
|
39
|
+
const pkg = shQuote(`${packageName}@${spec.version}`);
|
|
40
|
+
const lines = [
|
|
41
|
+
"#!/bin/sh",
|
|
42
|
+
"# Starts Slopify quietly when you log in: no browser tab, open it from your bookmark.",
|
|
43
|
+
`# ${launcherMarker} → General → "Start Slopify when I log in".`,
|
|
44
|
+
"# Turning that switch off deletes this file and the login entry that runs it.",
|
|
45
|
+
`exec >>${shQuote(spec.log)} 2>&1`,
|
|
46
|
+
`echo "$(date) Starting Slopify at login"`,
|
|
47
|
+
`NODE=${shQuote(spec.node)}`,
|
|
48
|
+
'[ -x "$NODE" ] || NODE=$(command -v node) || {',
|
|
49
|
+
` echo "Slopify could not start at login: Node.js was not found. Install Node.js from https://nodejs.org, then start Slopify once with npx ${packageName}."`,
|
|
50
|
+
" exit 1",
|
|
51
|
+
"}",
|
|
52
|
+
'PATH=$(dirname "$NODE"):$PATH',
|
|
53
|
+
"export PATH",
|
|
54
|
+
];
|
|
55
|
+
if (spec.entry !== null)
|
|
56
|
+
lines.push(`if [ -f ${shQuote(spec.entry)} ]; then exec "$NODE" ${shQuote(spec.entry)} ${args}; fi`);
|
|
57
|
+
if (spec.npxCli !== null)
|
|
58
|
+
lines.push(`if [ -f ${shQuote(spec.npxCli)} ]; then exec "$NODE" ${shQuote(spec.npxCli)} --yes ${pkg} ${args}; fi`);
|
|
59
|
+
lines.push(`exec npx --yes ${pkg} ${args}`, "");
|
|
60
|
+
return lines.join("\n");
|
|
61
|
+
}
|
|
62
|
+
// cmd.exe: inside double quotes only `%` still expands, so it is doubled. A path can't hold a
|
|
63
|
+
// double quote on Windows, and `windowsPathProblem` refuses the rest before anything is written.
|
|
64
|
+
function cmdQuote(value) {
|
|
65
|
+
return `"${value.replaceAll("%", "%%")}"`;
|
|
66
|
+
}
|
|
67
|
+
export function cmdLauncher(spec) {
|
|
68
|
+
const args = launchArgs(spec)
|
|
69
|
+
.map((arg) => (/^[\w.:-]+$/u.test(arg) ? arg : cmdQuote(arg)))
|
|
70
|
+
.join(" ");
|
|
71
|
+
const pkg = `${packageName}@${spec.version}`;
|
|
72
|
+
const log = `>>${cmdQuote(spec.log)} 2>&1`;
|
|
73
|
+
const nodeDir = spec.node.slice(0, Math.max(spec.node.lastIndexOf("\\"), 0));
|
|
74
|
+
const lines = [
|
|
75
|
+
"@echo off",
|
|
76
|
+
"rem Starts Slopify quietly when you log in: no browser tab, open it from your bookmark.",
|
|
77
|
+
`rem ${launcherMarker} > General > "Start Slopify when I log in".`,
|
|
78
|
+
"rem Turning that switch off deletes this file and the login entry that runs it.",
|
|
79
|
+
"rem Closing this window stops Slopify.",
|
|
80
|
+
"chcp 65001 >nul",
|
|
81
|
+
"title Slopify",
|
|
82
|
+
`set "NODE=${spec.node.replaceAll("%", "%%")}"`,
|
|
83
|
+
`set "PATH=${nodeDir.replaceAll("%", "%%")};%PATH%"`,
|
|
84
|
+
'if not exist "%NODE%" set "NODE=node"',
|
|
85
|
+
];
|
|
86
|
+
if (spec.entry !== null)
|
|
87
|
+
lines.push(`if not exist ${cmdQuote(spec.entry)} goto npxcli`, `"%NODE%" ${cmdQuote(spec.entry)} ${args} ${log}`, "exit /b");
|
|
88
|
+
lines.push(":npxcli");
|
|
89
|
+
if (spec.npxCli !== null)
|
|
90
|
+
lines.push(`if not exist ${cmdQuote(spec.npxCli)} goto npx`, `"%NODE%" ${cmdQuote(spec.npxCli)} --yes ${pkg} ${args} ${log}`, "exit /b");
|
|
91
|
+
lines.push(":npx", `call npx --yes ${pkg} ${args} ${log}`, "");
|
|
92
|
+
return lines.join("\r\n");
|
|
93
|
+
}
|
|
94
|
+
// The registry's Run value: a minimised console running the launcher, so a login shows a
|
|
95
|
+
// taskbar button named Slopify rather than a window in the way. REG_SZ doesn't expand
|
|
96
|
+
// variables, and `start` takes the first quoted argument as the window title.
|
|
97
|
+
export function windowsRunCommand(launcher) {
|
|
98
|
+
return `cmd.exe /d /c start "Slopify" /min "${launcher}"`;
|
|
99
|
+
}
|
|
100
|
+
// Characters cmd.exe would read as its own inside the Run value (`%` expands even in quotes
|
|
101
|
+
// there, where a batch file's `%%` doesn't apply). Windows paths can't contain `"`.
|
|
102
|
+
export function windowsPathProblem(path) {
|
|
103
|
+
return /[%"\r\n]/u.test(path)
|
|
104
|
+
? `The Slopify data folder ${path} contains a % sign, which Windows' start-up list can't run. Start Slopify with another folder (npx ${packageName} --data-dir C:\\Slopify) and turn the switch on there.`
|
|
105
|
+
: undefined;
|
|
106
|
+
}
|
|
107
|
+
// Desktop Entry spec: an argument with spaces or reserved characters is double-quoted, with
|
|
108
|
+
// `"`, `` ` ``, `$` and `\` backslash-escaped inside; the value as a whole then escapes `\`
|
|
109
|
+
// again, and `%` is a field code unless doubled.
|
|
110
|
+
export function desktopExecArg(value) {
|
|
111
|
+
if (/^[A-Za-z0-9_./+,:@=-]+$/u.test(value))
|
|
112
|
+
return value;
|
|
113
|
+
const quoted = `"${value.replace(/["`$\\]/gu, (char) => `\\${char}`)}"`;
|
|
114
|
+
return quoted.replaceAll("\\", "\\\\").replaceAll("%", "%%");
|
|
115
|
+
}
|
|
116
|
+
// Freedesktop keys escape newlines, tabs and backslashes in plain string values.
|
|
117
|
+
function desktopString(value) {
|
|
118
|
+
return value
|
|
119
|
+
.replaceAll("\\", "\\\\")
|
|
120
|
+
.replaceAll("\n", "\\n")
|
|
121
|
+
.replaceAll("\t", "\\t")
|
|
122
|
+
.replaceAll("\r", "\\r");
|
|
123
|
+
}
|
|
124
|
+
export function desktopEntry(launcher) {
|
|
125
|
+
return [
|
|
126
|
+
"[Desktop Entry]",
|
|
127
|
+
"Type=Application",
|
|
128
|
+
"Name=Slopify",
|
|
129
|
+
desktopString(`Comment=Starts Slopify quietly when you log in. Turn it off in Slopify: Settings → General.`),
|
|
130
|
+
`Exec=/bin/sh ${desktopExecArg(launcher)}`,
|
|
131
|
+
"Terminal=false",
|
|
132
|
+
"NoDisplay=true",
|
|
133
|
+
"Hidden=false",
|
|
134
|
+
"X-GNOME-Autostart-enabled=true",
|
|
135
|
+
desktopMarker,
|
|
136
|
+
"",
|
|
137
|
+
].join("\n");
|
|
138
|
+
}
|
|
139
|
+
export function xmlText(value) {
|
|
140
|
+
return value
|
|
141
|
+
.replaceAll("&", "&")
|
|
142
|
+
.replaceAll("<", "<")
|
|
143
|
+
.replaceAll(">", ">")
|
|
144
|
+
.replaceAll('"', """);
|
|
145
|
+
}
|
|
146
|
+
// A per-user LaunchAgent: launchd loads every agent in ~/Library/LaunchAgents at login and
|
|
147
|
+
// RunAtLoad starts it once. No KeepAlive, so quitting Slopify keeps it quit until next login.
|
|
148
|
+
export function launchAgent(launcher, log) {
|
|
149
|
+
return [
|
|
150
|
+
'<?xml version="1.0" encoding="UTF-8"?>',
|
|
151
|
+
'<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">',
|
|
152
|
+
`<!-- ${launcherMarker} → General → "Start Slopify when I log in". -->`,
|
|
153
|
+
'<plist version="1.0">',
|
|
154
|
+
"<dict>",
|
|
155
|
+
" <key>Label</key>",
|
|
156
|
+
` <string>${agentLabel}</string>`,
|
|
157
|
+
" <key>ProgramArguments</key>",
|
|
158
|
+
" <array>",
|
|
159
|
+
" <string>/bin/sh</string>",
|
|
160
|
+
` <string>${xmlText(launcher)}</string>`,
|
|
161
|
+
" </array>",
|
|
162
|
+
" <key>RunAtLoad</key>",
|
|
163
|
+
" <true/>",
|
|
164
|
+
" <key>KeepAlive</key>",
|
|
165
|
+
" <false/>",
|
|
166
|
+
" <key>ProcessType</key>",
|
|
167
|
+
" <string>Interactive</string>",
|
|
168
|
+
" <key>StandardOutPath</key>",
|
|
169
|
+
` <string>${xmlText(log)}</string>`,
|
|
170
|
+
" <key>StandardErrorPath</key>",
|
|
171
|
+
` <string>${xmlText(log)}</string>`,
|
|
172
|
+
"</dict>",
|
|
173
|
+
"</plist>",
|
|
174
|
+
"",
|
|
175
|
+
].join("\n");
|
|
176
|
+
}
|