@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
|
@@ -7,9 +7,12 @@ import { agentImageTimeoutMs, } from "../../kernel/ports/image.js";
|
|
|
7
7
|
import { providerError } from "../../kernel/ports/model.js";
|
|
8
8
|
import { cliReported, quoted, refusedImage } from "../explain.js";
|
|
9
9
|
import { cliLoginError } from "../llm/cli-login-error.js";
|
|
10
|
-
import {
|
|
10
|
+
import { codexLimitError, readingOf } from "../llm/codex.js";
|
|
11
|
+
import { codexPlanLimit } from "../llm/codex-limits.js";
|
|
12
|
+
import { cliEvent, cliShaped, endedKind, endedWithout, stopCliRun, } from "../llm/run-cli.js";
|
|
11
13
|
import { lines } from "../llm/sse-lines.js";
|
|
12
14
|
import { codexGeneratedImage, codexImageCount } from "./codex-output.js";
|
|
15
|
+
import { referencePictures } from "./reference.js";
|
|
13
16
|
// "Codex default" puts no model and no effort on the command line, so the CLI's own defaults
|
|
14
17
|
// draw the image; it is what every project saved before the choice existed runs. Every other
|
|
15
18
|
// model id is one of the Codex CLI's own models - the list the Codex text provider shows -
|
|
@@ -41,7 +44,9 @@ const alwaysDisabled = [
|
|
|
41
44
|
"workspace_dependencies",
|
|
42
45
|
];
|
|
43
46
|
function reviews(req) {
|
|
44
|
-
return (req.model !== codexImageModel.id ||
|
|
47
|
+
return (req.model !== codexImageModel.id ||
|
|
48
|
+
req.thinking !== undefined ||
|
|
49
|
+
referencePictures(req).length > 0);
|
|
45
50
|
}
|
|
46
51
|
// The binary's image tool (`ImagegenArgs`: prompt, referenced_image_paths,
|
|
47
52
|
// num_last_images_to_include) takes each referenced image as an absolute path it reads
|
|
@@ -49,15 +54,41 @@ function reviews(req) {
|
|
|
49
54
|
export function codexReferencePath(directory, image) {
|
|
50
55
|
return join(directory, image.mime === "image/jpeg" ? "reference.jpg" : "reference.png");
|
|
51
56
|
}
|
|
52
|
-
|
|
57
|
+
// Every picture the request carries, each at its own path: the establishing image keeps the
|
|
58
|
+
// name it always had, the cast pictures follow it as reference-2, reference-3 and so on.
|
|
59
|
+
export function codexReferencePaths(directory, req) {
|
|
60
|
+
return referencePictures(req).map((picture, index) => ({
|
|
61
|
+
picture,
|
|
62
|
+
path: index === 0
|
|
63
|
+
? codexReferencePath(directory, picture.image)
|
|
64
|
+
: join(directory, `reference-${String(index + 1)}${picture.image.mime === "image/jpeg" ? ".jpg" : ".png"}`),
|
|
65
|
+
}));
|
|
66
|
+
}
|
|
67
|
+
function castInstructions(req, directory) {
|
|
68
|
+
const paths = codexReferencePaths(directory, req);
|
|
69
|
+
return [
|
|
70
|
+
`Reference images are saved at these paths. Pass all of them in referenced_image_paths on every image generation call: ${paths.map((row) => row.path).join(", ")}.`,
|
|
71
|
+
...paths.map((row) => row.picture.member === undefined
|
|
72
|
+
? `${row.path} is the establishing image: keep its characters, rendering style and colour palette.`
|
|
73
|
+
: `${row.path} shows ${row.picture.member}${describeMember(req, row.picture.member)}: draw ${row.picture.member} to look exactly like this.`),
|
|
74
|
+
"Use them as references only: do not copy their composition, pose or framing; compose this image from the brief.",
|
|
75
|
+
].join(" ");
|
|
76
|
+
}
|
|
77
|
+
function describeMember(req, name) {
|
|
78
|
+
const description = req.cast?.find((member) => member.name === name)?.description.trim() ?? "";
|
|
79
|
+
return description === "" ? "" : ` (${description})`;
|
|
80
|
+
}
|
|
81
|
+
export function codexImageInstructions(req, reference, directory) {
|
|
53
82
|
return [
|
|
54
83
|
"You are making one finished image for Slopify, a video creation app, with the image generation tool.",
|
|
55
84
|
"Fidelity to the brief comes first. Before calling the tool, write its prompt yourself as a detailed, faithful visual description of the brief: the subject and what it is doing, the setting, composition and framing for the target aspect ratio, lighting, colour palette, style and mood, and any text that must appear, spelled exactly. Take every element from the brief and keep its wording where it is specific. Do not add subjects, text, logos or story the brief does not ask for, and do not pad the prompt with generic quality words.",
|
|
56
|
-
...(
|
|
57
|
-
? []
|
|
58
|
-
:
|
|
59
|
-
|
|
60
|
-
|
|
85
|
+
...(req.cast !== undefined && req.cast.length > 0 && directory !== undefined
|
|
86
|
+
? [castInstructions(req, directory)]
|
|
87
|
+
: reference === undefined
|
|
88
|
+
? []
|
|
89
|
+
: [
|
|
90
|
+
`A reference image is saved at ${reference}. Pass exactly that path in referenced_image_paths on every image generation call. Use it as the reference for style, characters and palette: keep the same characters, rendering style and colour palette, but do not copy its composition, pose or framing; compose this image from the brief.`,
|
|
91
|
+
]),
|
|
61
92
|
"Take the time you need. After each image, look at it and compare it with the brief; if anything is missing, wrong or distorted, revise the prompt and generate again. Deliver exactly one final image: the last image you generate is the one Slopify uses, so stop once it matches the brief.",
|
|
62
93
|
"Let the image generation tool save its output in its default location. Slopify will collect it. Use PNG or JPEG. Do not copy, rename, edit or create any other file, and do not put the image or a link in your reply.",
|
|
63
94
|
`Target aspect ratio: ${req.aspect}.`,
|
|
@@ -115,10 +146,19 @@ export function codexImageArgs(req, directory) {
|
|
|
115
146
|
: ["-c", `model_reasoning_effort="${req.thinking === "off" ? "none" : req.thinking}"`]),
|
|
116
147
|
...(req.model === codexImageModel.id ? [] : ["-m", req.model]),
|
|
117
148
|
"--",
|
|
118
|
-
codexImageInstructions(req, reference),
|
|
149
|
+
codexImageInstructions(req, reference, directory),
|
|
119
150
|
];
|
|
120
151
|
}
|
|
121
152
|
const failure = z.object({ error: z.object({ message: z.string() }) });
|
|
153
|
+
const turnUsage = z.object({
|
|
154
|
+
usage: z
|
|
155
|
+
.object({
|
|
156
|
+
input_tokens: z.number(),
|
|
157
|
+
output_tokens: z.number(),
|
|
158
|
+
cached_input_tokens: z.number().optional(),
|
|
159
|
+
})
|
|
160
|
+
.nullish(),
|
|
161
|
+
});
|
|
122
162
|
const errorEvent = z.object({ message: z.string() });
|
|
123
163
|
const item = z.object({ item: z.object({ type: z.string(), text: z.string().optional() }) });
|
|
124
164
|
export function codexImage(deps) {
|
|
@@ -140,7 +180,7 @@ export function codexImage(deps) {
|
|
|
140
180
|
if (req.model.trim() === "" || req.model.startsWith("-"))
|
|
141
181
|
throw providerError({
|
|
142
182
|
kind: "unsupported",
|
|
143
|
-
message: "No Codex model is chosen for images. Choose one (or Codex default) for images in the Providers section of Edit project, then use
|
|
183
|
+
message: "No Codex model is chosen for images. Choose one (or Codex default) for images in the Providers section of Edit project, then use Try again.",
|
|
144
184
|
});
|
|
145
185
|
req.signal.throwIfAborted();
|
|
146
186
|
const directory = mkdtempSync(join(tmpdir(), "slopify-codex-image-"));
|
|
@@ -149,6 +189,8 @@ export function codexImage(deps) {
|
|
|
149
189
|
let threadId;
|
|
150
190
|
let run;
|
|
151
191
|
let reported = -1;
|
|
192
|
+
let usage;
|
|
193
|
+
const before = deps.readLimits?.().catch(() => null);
|
|
152
194
|
// A line for the stage's live panel each time this thread's image count moves.
|
|
153
195
|
const report = () => {
|
|
154
196
|
if (req.onProgress === undefined)
|
|
@@ -167,10 +209,8 @@ export function codexImage(deps) {
|
|
|
167
209
|
}
|
|
168
210
|
};
|
|
169
211
|
try {
|
|
170
|
-
|
|
171
|
-
writeFileSync(
|
|
172
|
-
mode: 0o600,
|
|
173
|
-
});
|
|
212
|
+
for (const { path, picture } of codexReferencePaths(directory, req))
|
|
213
|
+
writeFileSync(path, picture.image.bytes, { mode: 0o600 });
|
|
174
214
|
report();
|
|
175
215
|
try {
|
|
176
216
|
run = deps.run(binary, codexImageArgs(req, directory), req.signal, {
|
|
@@ -181,7 +221,7 @@ export function codexImage(deps) {
|
|
|
181
221
|
catch {
|
|
182
222
|
throw providerError({
|
|
183
223
|
kind: "unsupported",
|
|
184
|
-
message: "The Codex CLI could not be started. Check it is installed and set up in Settings → Providers, then use
|
|
224
|
+
message: "The Codex CLI could not be started. Check it is installed and set up in Settings → Providers, then use Try again.",
|
|
185
225
|
});
|
|
186
226
|
}
|
|
187
227
|
let completed = false;
|
|
@@ -198,17 +238,30 @@ export function codexImage(deps) {
|
|
|
198
238
|
if (threadId !== undefined)
|
|
199
239
|
throw providerError({
|
|
200
240
|
kind: "unavailable",
|
|
201
|
-
message: "The Codex CLI started more than one image job for one image, so Slopify cannot tell which result is right (both may have used your quota). Use
|
|
241
|
+
message: "The Codex CLI started more than one image job for one image, so Slopify cannot tell which result is right (both may have used your quota). Use Try again to make the image again.",
|
|
202
242
|
});
|
|
203
243
|
threadId = cliShaped(binary, z.object({ thread_id: z.string() }), event.value).thread_id;
|
|
204
244
|
}
|
|
205
|
-
else if (event.type === "turn.completed")
|
|
245
|
+
else if (event.type === "turn.completed") {
|
|
206
246
|
completed = true;
|
|
247
|
+
const counted = turnUsage.safeParse(event.value);
|
|
248
|
+
if (counted.success && counted.data.usage)
|
|
249
|
+
usage = {
|
|
250
|
+
inputTokens: counted.data.usage.input_tokens,
|
|
251
|
+
outputTokens: counted.data.usage.output_tokens,
|
|
252
|
+
...(counted.data.usage.cached_input_tokens
|
|
253
|
+
? { cachedInputTokens: counted.data.usage.cached_input_tokens }
|
|
254
|
+
: {}),
|
|
255
|
+
};
|
|
256
|
+
}
|
|
207
257
|
else if (event.type === "turn.failed") {
|
|
208
258
|
const message = cliShaped(binary, failure, event.value).error.message;
|
|
209
259
|
const login = cliLoginError("codex", message);
|
|
210
260
|
if (login)
|
|
211
261
|
throw login;
|
|
262
|
+
const planLimit = codexPlanLimit(message, new Date());
|
|
263
|
+
if (planLimit !== undefined)
|
|
264
|
+
throw codexLimitError(message, planLimit);
|
|
212
265
|
throw providerError({
|
|
213
266
|
kind: /refus|content.policy|safety/i.test(message) ? "refusal" : "other",
|
|
214
267
|
message: /refus|content.policy|safety/i.test(message)
|
|
@@ -221,6 +274,9 @@ export function codexImage(deps) {
|
|
|
221
274
|
const login = cliLoginError("codex", message);
|
|
222
275
|
if (login)
|
|
223
276
|
throw login;
|
|
277
|
+
const planLimit = codexPlanLimit(message, new Date());
|
|
278
|
+
if (planLimit !== undefined)
|
|
279
|
+
throw codexLimitError(message, planLimit);
|
|
224
280
|
throw providerError({
|
|
225
281
|
kind: /image.generation|image tool|feature.*unavailable/i.test(message)
|
|
226
282
|
? "unsupported"
|
|
@@ -243,12 +299,15 @@ export function codexImage(deps) {
|
|
|
243
299
|
if (ended.error !== null)
|
|
244
300
|
throw providerError({
|
|
245
301
|
kind: "unsupported",
|
|
246
|
-
message: "The Codex CLI could not be started. Check it is installed and set up in Settings → Providers, then use
|
|
302
|
+
message: "The Codex CLI could not be started. Check it is installed and set up in Settings → Providers, then use Try again.",
|
|
247
303
|
});
|
|
304
|
+
const stderrLimit = codexPlanLimit(run.stderr(), new Date());
|
|
305
|
+
if ((ended.code !== 0 || !completed) && stderrLimit !== undefined)
|
|
306
|
+
throw codexLimitError(run.stderr().trim(), stderrLimit);
|
|
248
307
|
if (ended.code !== 0 || !completed)
|
|
249
308
|
throw (cliLoginError("codex", run.stderr()) ??
|
|
250
309
|
providerError({
|
|
251
|
-
kind: unavailable ? "unsupported" :
|
|
310
|
+
kind: unavailable ? "unsupported" : endedKind(ended),
|
|
252
311
|
message: endedWithout(binary, ended, run.stderr()),
|
|
253
312
|
}));
|
|
254
313
|
if (unavailable)
|
|
@@ -256,7 +315,13 @@ export function codexImage(deps) {
|
|
|
256
315
|
kind: "unsupported",
|
|
257
316
|
message: `The Codex CLI cannot make images. ${cannotDraw}`,
|
|
258
317
|
});
|
|
259
|
-
|
|
318
|
+
const image = codexGeneratedImage(env, threadId, startedAt);
|
|
319
|
+
const limits = await readingOf(before, deps.readLimits);
|
|
320
|
+
return {
|
|
321
|
+
...image,
|
|
322
|
+
...(usage === undefined ? {} : { usage }),
|
|
323
|
+
...(limits === undefined ? {} : { limits }),
|
|
324
|
+
};
|
|
260
325
|
}
|
|
261
326
|
catch (error) {
|
|
262
327
|
req.signal.throwIfAborted();
|
|
@@ -281,12 +346,12 @@ async function* bounded(source, maximum) {
|
|
|
281
346
|
if (size > maximum)
|
|
282
347
|
throw providerError({
|
|
283
348
|
kind: "other",
|
|
284
|
-
message: "The Codex CLI sent far more output than an image job should, so Slopify stopped it. Use
|
|
349
|
+
message: "The Codex CLI sent far more output than an image job should, so Slopify stopped it. Use Try again; if it keeps happening, update the Codex CLI to the latest version.",
|
|
285
350
|
});
|
|
286
351
|
yield chunk;
|
|
287
352
|
}
|
|
288
353
|
}
|
|
289
|
-
const imageNext = "Use
|
|
354
|
+
const imageNext = "Use Try again; if it keeps failing, run codex in a terminal to check it works and is signed in, or choose another image provider in the Providers section of Edit project.";
|
|
290
355
|
const cannotDraw = "Update the Codex CLI and check your ChatGPT plan includes image generation, or choose another image provider in the Providers section of Edit project.";
|
|
291
356
|
function imageEnvironment(directory, source) {
|
|
292
357
|
const env = { PWD: directory };
|
|
@@ -4,7 +4,7 @@ import { providerError } from "../../kernel/ports/model.js";
|
|
|
4
4
|
import { httpFailure, internalError, missingKey, noImage, providerSaid, unreadable, } from "../explain.js";
|
|
5
5
|
import { retryAfter } from "../retry-after.js";
|
|
6
6
|
import { downloadImage } from "./bytes.js";
|
|
7
|
-
import {
|
|
7
|
+
import { referencePictures, withReferences } from "./reference.js";
|
|
8
8
|
import { dataUri, downloadVideo } from "./video.js";
|
|
9
9
|
// The HTTP gateway adapter for fal.ai: `fetch` and the downloader beside this file, no SDK.
|
|
10
10
|
// `@fal-ai/client` would buy queue polling, which only the video clips need and which is three
|
|
@@ -102,7 +102,10 @@ export function falImage(deps) {
|
|
|
102
102
|
generate: async (req) => {
|
|
103
103
|
// An establishing image goes to the model's edit endpoint, which takes input images as
|
|
104
104
|
// `image_urls`; only the models that have one are marked as taking a reference.
|
|
105
|
-
|
|
105
|
+
// Cast pictures ride the same list; a model without an edit endpoint gets the cast in
|
|
106
|
+
// words instead, since only the establishing image is something the user turned on.
|
|
107
|
+
const pictures = referencePictures(req);
|
|
108
|
+
const edit = pictures.length === 0 ? undefined : editEndpoint(req.model);
|
|
106
109
|
if (req.reference !== undefined && edit === undefined)
|
|
107
110
|
throw providerError({
|
|
108
111
|
kind: "unsupported",
|
|
@@ -113,8 +116,10 @@ export function falImage(deps) {
|
|
|
113
116
|
signal: req.signal,
|
|
114
117
|
headers: { Authorization: `Key ${keyOf(deps)}`, "Content-Type": "application/json" },
|
|
115
118
|
body: JSON.stringify({
|
|
116
|
-
prompt:
|
|
117
|
-
...(
|
|
119
|
+
prompt: withReferences(req.prompt, req, edit === undefined ? 0 : pictures.length),
|
|
120
|
+
...(edit === undefined
|
|
121
|
+
? {}
|
|
122
|
+
: { image_urls: pictures.map((picture) => dataUri(picture.image)) }),
|
|
118
123
|
...aspectOf(req.model, req.aspect),
|
|
119
124
|
// The stage sends Number as that many independent calls, one piece each.
|
|
120
125
|
num_images: 1,
|
|
@@ -167,7 +172,7 @@ export function falImage(deps) {
|
|
|
167
172
|
if (current.error)
|
|
168
173
|
throw providerError({
|
|
169
174
|
kind: "other",
|
|
170
|
-
message: providerSaid("fal.ai", "could not make the video clip", redact(current.error), "Use
|
|
175
|
+
message: providerSaid("fal.ai", "could not make the video clip", redact(current.error), "Use Try again; if it keeps happening, choose another image-to-video model under Animate images in Edit project → Inputs → Look."),
|
|
171
176
|
});
|
|
172
177
|
if (current.status === "COMPLETED")
|
|
173
178
|
break;
|
|
@@ -223,6 +228,9 @@ function kindOf(status) {
|
|
|
223
228
|
// ceiling: everything else is `other` and is retried, so a 422 naming an input this model
|
|
224
229
|
// does not take fails the same way four times over. A terminal "this will never work" kind
|
|
225
230
|
// has to reach the port's error contract first, which is not this adapter's to widen.
|
|
231
|
+
// The provider's own server failed; the same request may well succeed later.
|
|
232
|
+
if (status >= 500)
|
|
233
|
+
return "dropped";
|
|
226
234
|
return "other";
|
|
227
235
|
}
|
|
228
236
|
async function failure(response, subject = "image request") {
|
|
@@ -5,7 +5,7 @@ import { httpFailure, missingKey, noImage, quoted, refusedImage, unreadable } fr
|
|
|
5
5
|
import { retryAfter } from "../retry-after.js";
|
|
6
6
|
import { describeBytes, sniffImage } from "./bytes.js";
|
|
7
7
|
import { discoverGoogleImages } from "./models.js";
|
|
8
|
-
import {
|
|
8
|
+
import { referencePictures, withReferences } from "./reference.js";
|
|
9
9
|
// The HTTP gateway adapter for Google's own image generation, billed to a Gemini API key
|
|
10
10
|
// rather than to a host reselling the same models. Like the OpenAI one it hands back the
|
|
11
11
|
// bytes with the call, so there is no link to follow.
|
|
@@ -65,15 +65,15 @@ export function googleImage(deps) {
|
|
|
65
65
|
model: req.model,
|
|
66
66
|
// With an establishing image the input is two parts, the image first; the Gemini
|
|
67
67
|
// image models draw with an input image as their reference.
|
|
68
|
-
input: req.
|
|
68
|
+
input: referencePictures(req).length === 0
|
|
69
69
|
? req.prompt
|
|
70
70
|
: [
|
|
71
|
-
{
|
|
71
|
+
...referencePictures(req).map(({ image }) => ({
|
|
72
72
|
type: "image",
|
|
73
|
-
mime_type:
|
|
74
|
-
data: Buffer.from(
|
|
75
|
-
},
|
|
76
|
-
{ type: "text", text:
|
|
73
|
+
mime_type: image.mime,
|
|
74
|
+
data: Buffer.from(image.bytes).toString("base64"),
|
|
75
|
+
})),
|
|
76
|
+
{ type: "text", text: withReferences(req.prompt, req) },
|
|
77
77
|
],
|
|
78
78
|
// The stage sends Number as that many independent calls, one piece each, so one
|
|
79
79
|
// image per request is what it asks for. Nothing about style is set: the stage
|
|
@@ -117,7 +117,7 @@ function decode(part) {
|
|
|
117
117
|
const claimed = part.mime_type ?? "nothing";
|
|
118
118
|
throw providerError({
|
|
119
119
|
kind: "other",
|
|
120
|
-
message: `Google sent back something that is not a PNG or JPEG image (labelled ${claimed}, but ${describeBytes(bytes)}). Use
|
|
120
|
+
message: `Google sent back something that is not a PNG or JPEG image (labelled ${claimed}, but ${describeBytes(bytes)}). Use Try again; if it keeps happening, choose another image model in the Providers section of Edit project.`,
|
|
121
121
|
});
|
|
122
122
|
}
|
|
123
123
|
return { bytes, mime };
|
|
@@ -155,6 +155,9 @@ function kindOf(status, named, message) {
|
|
|
155
155
|
// ceiling: everything else is `other` and is retried, so a prompt past the model's limit
|
|
156
156
|
// fails the same way four times over. A terminal "this will never work" kind has to reach
|
|
157
157
|
// the port's error contract first, which is not this adapter's to widen.
|
|
158
|
+
// The provider's own server failed; the same request may well succeed later.
|
|
159
|
+
if (status >= 500)
|
|
160
|
+
return "dropped";
|
|
158
161
|
return "other";
|
|
159
162
|
}
|
|
160
163
|
async function failure(response) {
|
|
@@ -5,7 +5,7 @@ import { httpFailure, missingKey, noImage, refusedImage, unreadable } from "../e
|
|
|
5
5
|
import { retryAfter } from "../retry-after.js";
|
|
6
6
|
import { describeBytes, sniffImage } from "./bytes.js";
|
|
7
7
|
import { discoverOpenAiImages } from "./models.js";
|
|
8
|
-
import {
|
|
8
|
+
import { referencePictures, withReferences } from "./reference.js";
|
|
9
9
|
// The HTTP gateway adapter for OpenAI's images endpoint: the platform's own `fetch` and
|
|
10
10
|
// nothing else, because the whole call is one request. Unlike fal and Replicate this one
|
|
11
11
|
// hands back the image itself - a GPT image model always answers with base64, never a URL -
|
|
@@ -54,7 +54,8 @@ export function openAiImage(deps) {
|
|
|
54
54
|
id: "openai-image",
|
|
55
55
|
models: () => discoverOpenAiImages(deps),
|
|
56
56
|
generate: async (req) => {
|
|
57
|
-
const
|
|
57
|
+
const pictures = referencePictures(req);
|
|
58
|
+
const response = pictures.length === 0
|
|
58
59
|
? await deps.fetch(`${openAiImagesBase}/images/generations`, {
|
|
59
60
|
method: "POST",
|
|
60
61
|
signal: req.signal,
|
|
@@ -79,7 +80,7 @@ export function openAiImage(deps) {
|
|
|
79
80
|
method: "POST",
|
|
80
81
|
signal: req.signal,
|
|
81
82
|
headers: { Authorization: `Bearer ${keyOf(deps)}` },
|
|
82
|
-
body: editForm(req,
|
|
83
|
+
body: editForm(req, pictures),
|
|
83
84
|
});
|
|
84
85
|
if (!response.ok) {
|
|
85
86
|
throw await failure(response);
|
|
@@ -92,13 +93,16 @@ export function openAiImage(deps) {
|
|
|
92
93
|
},
|
|
93
94
|
};
|
|
94
95
|
}
|
|
95
|
-
|
|
96
|
+
// The edits endpoint takes several input images as repeated `image[]` parts: the establishing
|
|
97
|
+
// image first, then the cast pictures.
|
|
98
|
+
function editForm(req, pictures) {
|
|
96
99
|
const form = new FormData();
|
|
97
100
|
form.set("model", req.model);
|
|
98
|
-
form.set("prompt",
|
|
101
|
+
form.set("prompt", withReferences(req.prompt, req));
|
|
99
102
|
form.set("n", "1");
|
|
100
103
|
form.set("size", sizeFor(req.model, req.aspect));
|
|
101
|
-
|
|
104
|
+
for (const [index, { image }] of pictures.entries())
|
|
105
|
+
form.append("image[]", new Blob([Uint8Array.from(image.bytes)], { type: image.mime }), `${index === 0 ? "reference" : `reference-${String(index + 1)}`}${image.mime === "image/jpeg" ? ".jpg" : ".png"}`);
|
|
102
106
|
return form;
|
|
103
107
|
}
|
|
104
108
|
// A GPT image model always answers with base64, so the bytes arrive with the call and
|
|
@@ -110,7 +114,7 @@ function decode(b64) {
|
|
|
110
114
|
if (mime === undefined) {
|
|
111
115
|
throw providerError({
|
|
112
116
|
kind: "other",
|
|
113
|
-
message: `OpenAI sent back something that is not a PNG or JPEG image (${describeBytes(bytes)}). Use
|
|
117
|
+
message: `OpenAI sent back something that is not a PNG or JPEG image (${describeBytes(bytes)}). Use Try again; if it keeps happening, choose another image model in the Providers section of Edit project.`,
|
|
114
118
|
});
|
|
115
119
|
}
|
|
116
120
|
return { bytes, mime };
|
|
@@ -137,6 +141,9 @@ function kindOf(status, code) {
|
|
|
137
141
|
// ceiling: everything else is `other` and is retried, so a prompt past the model's length
|
|
138
142
|
// limit fails the same way four times over. A terminal "this will never work" kind has to
|
|
139
143
|
// reach the port's error contract first, which is not this adapter's to widen.
|
|
144
|
+
// The provider's own server failed; the same request may well succeed later.
|
|
145
|
+
if (status >= 500)
|
|
146
|
+
return "dropped";
|
|
140
147
|
return "other";
|
|
141
148
|
}
|
|
142
149
|
async function failure(response) {
|
|
@@ -5,3 +5,37 @@ export const referenceNote = "The attached image is a visual reference only: kee
|
|
|
5
5
|
export function withReferenceNote(prompt, reference) {
|
|
6
6
|
return reference === undefined ? prompt : `${referenceNote}\n\n${prompt}`;
|
|
7
7
|
}
|
|
8
|
+
// Every picture a request carries, in the order they are attached: the establishing image,
|
|
9
|
+
// then each mentioned cast member's pictures.
|
|
10
|
+
export function referencePictures(req) {
|
|
11
|
+
return [
|
|
12
|
+
...(req.reference === undefined ? [] : [{ image: req.reference }]),
|
|
13
|
+
...(req.cast ?? []).flatMap((member) => member.images.map((image) => ({ image, member: member.name }))),
|
|
14
|
+
];
|
|
15
|
+
}
|
|
16
|
+
// The prompt a provider sends with the first `limit` of `referencePictures(req)` attached. A
|
|
17
|
+
// request without cast keeps exactly the wording it always had. Cast members whose pictures
|
|
18
|
+
// did not fit (a provider that takes fewer input images, or none) are described in words.
|
|
19
|
+
export function withReferences(prompt, req, limit = Number.POSITIVE_INFINITY) {
|
|
20
|
+
if (req.cast === undefined || req.cast.length === 0)
|
|
21
|
+
return limit === 0 ? prompt : withReferenceNote(prompt, req.reference);
|
|
22
|
+
const sent = referencePictures(req).slice(0, limit);
|
|
23
|
+
const lines = [];
|
|
24
|
+
if (sent.length > 0)
|
|
25
|
+
lines.push("The attached images are visual references only: do not copy their composition, pose or framing.");
|
|
26
|
+
for (const [index, picture] of sent.entries()) {
|
|
27
|
+
const member = req.cast.find((row) => row.name === picture.member);
|
|
28
|
+
lines.push(member === undefined
|
|
29
|
+
? `Image ${String(index + 1)} is the establishing image: keep its characters, rendering style and colour palette.`
|
|
30
|
+
: `Image ${String(index + 1)} shows ${member.name}${describe(member.description)}: draw ${member.name} to look exactly like this.`);
|
|
31
|
+
}
|
|
32
|
+
const shown = new Set(sent.flatMap((picture) => picture.member ?? []));
|
|
33
|
+
for (const member of req.cast)
|
|
34
|
+
if (!shown.has(member.name))
|
|
35
|
+
lines.push(`${member.name} appears in this image${describe(member.description)}.`);
|
|
36
|
+
lines.push("Compose a new image from this brief:");
|
|
37
|
+
return `${lines.join("\n")}\n\n${prompt}`;
|
|
38
|
+
}
|
|
39
|
+
function describe(description) {
|
|
40
|
+
return description.trim() === "" ? "" : ` (${description.trim()})`;
|
|
41
|
+
}
|
|
@@ -4,6 +4,7 @@ import { providerError } from "../../kernel/ports/model.js";
|
|
|
4
4
|
import { httpFailure, missingKey, noImage, providerSaid, refusedImage, unreadable, } from "../explain.js";
|
|
5
5
|
import { retryAfter } from "../retry-after.js";
|
|
6
6
|
import { downloadImage } from "./bytes.js";
|
|
7
|
+
import { withReferences } from "./reference.js";
|
|
7
8
|
import { dataUri, downloadVideo } from "./video.js";
|
|
8
9
|
// The HTTP gateway adapter for Replicate: `fetch`, the injected clock and the downloader
|
|
9
10
|
// beside this file, no SDK. The `replicate` package wraps exactly the two requests below, so
|
|
@@ -58,7 +59,8 @@ export function replicateImage(deps) {
|
|
|
58
59
|
},
|
|
59
60
|
body: JSON.stringify({
|
|
60
61
|
input: {
|
|
61
|
-
|
|
62
|
+
// These models take no input image, so the cast is described in words.
|
|
63
|
+
prompt: withReferences(req.prompt, req, 0),
|
|
62
64
|
// Replicate takes the aspect, so the closest size is exact.
|
|
63
65
|
aspect_ratio: req.aspect,
|
|
64
66
|
// The port stores PNG or JPEG and these models default to WebP. Nothing else
|
|
@@ -166,7 +168,7 @@ function declined(current) {
|
|
|
166
168
|
kind: refusal ? "refusal" : "other",
|
|
167
169
|
message: refusal
|
|
168
170
|
? refusedImage("Replicate", message)
|
|
169
|
-
: providerSaid("Replicate", "could not make the image", message, "Use
|
|
171
|
+
: providerSaid("Replicate", "could not make the image", message, "Use Try again; if it keeps happening, reword the image prompt in the Images section of Edit project or choose another image model in its Providers section."),
|
|
170
172
|
});
|
|
171
173
|
}
|
|
172
174
|
function auth(deps) {
|
|
@@ -188,6 +190,9 @@ function kindOf(status) {
|
|
|
188
190
|
// ceiling: everything else is `other` and is retried, so a 402 with no credit left fails the
|
|
189
191
|
// same way four times over. A terminal "this will never work" kind has to reach the port's
|
|
190
192
|
// error contract first, which is not this adapter's to widen.
|
|
193
|
+
// The provider's own server failed; the same request may well succeed later.
|
|
194
|
+
if (status >= 500)
|
|
195
|
+
return "dropped";
|
|
191
196
|
return "other";
|
|
192
197
|
}
|
|
193
198
|
async function failure(response, subject = "image request") {
|
|
@@ -16,13 +16,13 @@ export async function downloadVideo(download) {
|
|
|
16
16
|
// The link is not quoted back: a delivery link carries its own signature.
|
|
17
17
|
throw providerError({
|
|
18
18
|
kind: "other",
|
|
19
|
-
message: `${download.provider} made the video clip, but Slopify could not download it (error ${String(response.status)}). Check your internet connection, then use
|
|
19
|
+
message: `${download.provider} made the video clip, but Slopify could not download it (error ${String(response.status)}). Check your internet connection, then use Try again.`,
|
|
20
20
|
});
|
|
21
21
|
const bytes = new Uint8Array(await response.arrayBuffer());
|
|
22
22
|
if (!sniffVideo(bytes))
|
|
23
23
|
throw providerError({
|
|
24
24
|
kind: "other",
|
|
25
|
-
message: `${download.provider} sent back something that is not an MP4 video clip (${describeBytes(bytes)}). Use
|
|
25
|
+
message: `${download.provider} sent back something that is not an MP4 video clip (${describeBytes(bytes)}). Use Try again; if it keeps happening, choose another image-to-video model under Animate images in Edit project → Inputs → Look.`,
|
|
26
26
|
});
|
|
27
27
|
return { bytes, mime: "video/mp4" };
|
|
28
28
|
}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { cartesiaVersion } from "./tts/cartesia.js";
|
|
2
|
+
// Settings → Providers' Test button, per provider. Some answer a key they do not recognise
|
|
3
|
+
// with something other than 401: Google with 400 API_KEY_INVALID, ElevenLabs with 400
|
|
4
|
+
// invalid_api_key, Inworld with 403 "does not exist". Observed on 2026-09-27; their
|
|
5
|
+
// documentation says 401.
|
|
6
|
+
const bearer = (key) => ({ Authorization: `Bearer ${key}` });
|
|
7
|
+
// Health check → "Model reachable": one read per chosen model, or one of the list. A model's
|
|
8
|
+
// own page answers 404 when the key can't use it.
|
|
9
|
+
const openAiModel = {
|
|
10
|
+
url: (model) => `https://api.openai.com/v1/models/${encodeURIComponent(model)}`,
|
|
11
|
+
};
|
|
12
|
+
function listed(ids) {
|
|
13
|
+
return (body, model) => {
|
|
14
|
+
try {
|
|
15
|
+
const found = ids(JSON.parse(body));
|
|
16
|
+
return found.length === 0 ? undefined : found.includes(model);
|
|
17
|
+
}
|
|
18
|
+
catch {
|
|
19
|
+
return undefined;
|
|
20
|
+
}
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
const field = (value, key) => typeof value === "object" && value !== null ? value[key] : undefined;
|
|
24
|
+
const google = {
|
|
25
|
+
url: "https://generativelanguage.googleapis.com/v1beta/models?pageSize=1",
|
|
26
|
+
headers: (key) => ({ "x-goog-api-key": key }),
|
|
27
|
+
badKey: (status, body) => status === 400 && body.includes("API_KEY_INVALID"),
|
|
28
|
+
model: {
|
|
29
|
+
url: (model) => `https://generativelanguage.googleapis.com/v1beta/models/${encodeURIComponent(model.replace(/^models\//, ""))}`,
|
|
30
|
+
},
|
|
31
|
+
};
|
|
32
|
+
export const keyProbes = {
|
|
33
|
+
openrouter: {
|
|
34
|
+
url: "https://openrouter.ai/api/v1/key",
|
|
35
|
+
headers: bearer,
|
|
36
|
+
model: {
|
|
37
|
+
url: () => "https://openrouter.ai/api/v1/models",
|
|
38
|
+
lists: listed((body) => {
|
|
39
|
+
const data = field(body, "data");
|
|
40
|
+
return Array.isArray(data) ? data.map((one) => field(one, "id")) : [];
|
|
41
|
+
}),
|
|
42
|
+
},
|
|
43
|
+
},
|
|
44
|
+
"openai-tts": { url: "https://api.openai.com/v1/models", headers: bearer, model: openAiModel },
|
|
45
|
+
"openai-image": { url: "https://api.openai.com/v1/models", headers: bearer, model: openAiModel },
|
|
46
|
+
"google-image": google,
|
|
47
|
+
"google-tts": google,
|
|
48
|
+
// fal.ai's model list answers without a key; its price lookup requires one.
|
|
49
|
+
fal: {
|
|
50
|
+
url: "https://api.fal.ai/v1/models/pricing?endpoint_id=fal-ai/flux/dev",
|
|
51
|
+
headers: (key) => ({ Authorization: `Key ${key}` }),
|
|
52
|
+
model: {
|
|
53
|
+
url: (model) => `https://api.fal.ai/v1/models/pricing?endpoint_id=${encodeURIComponent(model)}`,
|
|
54
|
+
},
|
|
55
|
+
},
|
|
56
|
+
replicate: {
|
|
57
|
+
url: "https://api.replicate.com/v1/account",
|
|
58
|
+
headers: bearer,
|
|
59
|
+
// "owner/name", without a ":version".
|
|
60
|
+
model: {
|
|
61
|
+
url: (model) => `https://api.replicate.com/v1/models/${(model.split(":")[0] ?? model)
|
|
62
|
+
.split("/")
|
|
63
|
+
.map(encodeURIComponent)
|
|
64
|
+
.join("/")}`,
|
|
65
|
+
},
|
|
66
|
+
},
|
|
67
|
+
elevenlabs: {
|
|
68
|
+
url: "https://api.elevenlabs.io/v1/models",
|
|
69
|
+
headers: (key) => ({ "xi-api-key": key }),
|
|
70
|
+
badKey: (status, body) => status === 400 && body.includes("invalid_api_key"),
|
|
71
|
+
model: {
|
|
72
|
+
url: () => "https://api.elevenlabs.io/v1/models",
|
|
73
|
+
lists: listed((body) => Array.isArray(body) ? body.map((one) => field(one, "model_id")) : []),
|
|
74
|
+
},
|
|
75
|
+
},
|
|
76
|
+
cartesia: {
|
|
77
|
+
url: "https://api.cartesia.ai/voices?limit=1",
|
|
78
|
+
headers: (key) => ({ "X-API-Key": key, "Cartesia-Version": cartesiaVersion }),
|
|
79
|
+
},
|
|
80
|
+
inworld: {
|
|
81
|
+
url: "https://api.inworld.ai/voices/v1/voices?pageSize=1",
|
|
82
|
+
headers: (key) => ({ Authorization: `Basic ${key}` }),
|
|
83
|
+
badKey: (status, body) => status === 403 && /does not exist|was deleted/i.test(body),
|
|
84
|
+
},
|
|
85
|
+
};
|