@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
|
@@ -6,9 +6,11 @@ import { redact } from "../../kernel/log.js";
|
|
|
6
6
|
import { providerError } from "../../kernel/ports/model.js";
|
|
7
7
|
import { cliCheck, cliReported } from "../explain.js";
|
|
8
8
|
import { cliLoginError } from "./cli-login-error.js";
|
|
9
|
+
import { codexPlanLimit } from "./codex-limits.js";
|
|
9
10
|
import { nodeCodexModels } from "./codex-models.js";
|
|
10
11
|
import { documentServerName, documentTool, documentWorkspace, } from "./document-workspace.js";
|
|
11
|
-
import {
|
|
12
|
+
import { imageWorkspace } from "./image-workspace.js";
|
|
13
|
+
import { cliEvent, cliInput, cliShaped, deliveredInput, endedKind, endedWithout, promptOf, stopCliRun, stuckCli, } from "./run-cli.js";
|
|
12
14
|
import { lines } from "./sse-lines.js";
|
|
13
15
|
// The local-agent adapter for the Codex CLI. Same shape as Claude Code's and a different
|
|
14
16
|
// vocabulary: Codex writes a JSONL thread of `thread.started`, `item.*` and `turn.*`
|
|
@@ -45,7 +47,12 @@ const disabledFeatures = [
|
|
|
45
47
|
// plus a zero project-doc budget excludes AGENTS.md and project `.codex` context. Read-only is
|
|
46
48
|
// defense in depth if a future release exposes a new filesystem tool. The TOML strings keep their
|
|
47
49
|
// quotes because each `-c` value is parsed as TOML rather than as a shell expression.
|
|
48
|
-
|
|
50
|
+
//
|
|
51
|
+
// Pictures (a review's) are attached to the prompt with `--image` (`codex exec --help` 0.155.1:
|
|
52
|
+
// "-i, --image <FILE>... Optional image(s) to attach to the initial prompt"), which needs no
|
|
53
|
+
// tool: `view_image` stays disabled. Each is given as its own flag so a path is never read as
|
|
54
|
+
// the prompt.
|
|
55
|
+
export function codexArgs(req, directory, documents, images) {
|
|
49
56
|
return [
|
|
50
57
|
"exec",
|
|
51
58
|
"--json",
|
|
@@ -100,19 +107,29 @@ export function codexArgs(req, directory, documents) {
|
|
|
100
107
|
}).flatMap(([key, value]) => ["-c", `mcp_servers.${documentServerName}.${key}=${value}`]),
|
|
101
108
|
]
|
|
102
109
|
: []),
|
|
110
|
+
...(images?.files.flatMap((one) => ["--image", one.path]) ?? []),
|
|
103
111
|
// A literal '-' requests stdin; report bodies never become OS arguments.
|
|
104
112
|
"--",
|
|
105
113
|
"-",
|
|
106
114
|
];
|
|
107
115
|
}
|
|
116
|
+
const itemProgress = z.object({
|
|
117
|
+
item: z.object({ id: z.string().optional(), type: z.string(), text: z.string().optional() }),
|
|
118
|
+
});
|
|
108
119
|
const itemCompleted = z.object({
|
|
109
120
|
item: z.object({ type: z.string(), text: z.string().optional() }),
|
|
110
121
|
});
|
|
111
122
|
// The field names are the shipped binary's own: `TurnCompletedEvent` carries `usage`, and
|
|
112
123
|
// its counts are `input_tokens`, `cached_input_tokens`, `cache_write_input_tokens`,
|
|
113
|
-
// `output_tokens`, `reasoning_output_tokens`.
|
|
124
|
+
// `output_tokens`, `reasoning_output_tokens`. `input_tokens` already counts the cached ones.
|
|
114
125
|
const turnCompleted = z.object({
|
|
115
|
-
usage: z
|
|
126
|
+
usage: z
|
|
127
|
+
.object({
|
|
128
|
+
input_tokens: z.number(),
|
|
129
|
+
output_tokens: z.number(),
|
|
130
|
+
cached_input_tokens: z.number().optional(),
|
|
131
|
+
})
|
|
132
|
+
.nullish(),
|
|
116
133
|
});
|
|
117
134
|
const turnFailed = z.object({ error: z.object({ message: z.string() }) });
|
|
118
135
|
// The top-level `error` event carries its text in `message`, not in `error.message`.
|
|
@@ -123,13 +140,27 @@ export function codexLlm(deps) {
|
|
|
123
140
|
req.signal.throwIfAborted();
|
|
124
141
|
const workspace = codexWorkspace(deps.env ?? process.env);
|
|
125
142
|
let documents;
|
|
143
|
+
const typed = new Map();
|
|
144
|
+
let images;
|
|
126
145
|
let run;
|
|
127
146
|
let ended;
|
|
147
|
+
// Asked beside the run rather than before it: the read takes about a second and the
|
|
148
|
+
// turn spends nothing until its first answer, so the reading is still the one before.
|
|
149
|
+
const before = deps.readLimits?.().catch(() => null);
|
|
128
150
|
try {
|
|
129
151
|
documents = documentWorkspace(req.documents);
|
|
130
|
-
|
|
152
|
+
images = imageWorkspace(req.images);
|
|
153
|
+
run = deps.run(binary, codexArgs(req, workspace.directory, documents, images), req.signal, {
|
|
131
154
|
...workspace.options,
|
|
132
|
-
stdin: cliInput([
|
|
155
|
+
stdin: cliInput([
|
|
156
|
+
documents?.instructions,
|
|
157
|
+
images === undefined
|
|
158
|
+
? undefined
|
|
159
|
+
: `The attached images, in order:\n${images.files.map((one, index) => `${String(index + 1)}. ${one.name}`).join("\n")}`,
|
|
160
|
+
promptOf(req.messages),
|
|
161
|
+
]
|
|
162
|
+
.filter(Boolean)
|
|
163
|
+
.join("\n\n")),
|
|
133
164
|
});
|
|
134
165
|
for await (const line of lines(run.stdout, req.signal)) {
|
|
135
166
|
if (line.trim() === "") {
|
|
@@ -138,6 +169,21 @@ export function codexLlm(deps) {
|
|
|
138
169
|
const event = cliEvent(binary, line);
|
|
139
170
|
yield { type: "activity" };
|
|
140
171
|
req.signal.throwIfAborted();
|
|
172
|
+
// An agent message may be reported while it is written; its text so far is shown on
|
|
173
|
+
// the live panel, and only the completed item becomes the answer.
|
|
174
|
+
if (event.type === "item.started" || event.type === "item.updated") {
|
|
175
|
+
const item = itemProgress.safeParse(event.value);
|
|
176
|
+
if (item.success && item.data.item.type === "agent_message") {
|
|
177
|
+
const id = item.data.item.id ?? "";
|
|
178
|
+
const seen = typed.get(id) ?? "";
|
|
179
|
+
const text = item.data.item.text ?? "";
|
|
180
|
+
if (text.length > seen.length && text.startsWith(seen)) {
|
|
181
|
+
typed.set(id, text);
|
|
182
|
+
yield { type: "partial", text: text.slice(seen.length) };
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
continue;
|
|
186
|
+
}
|
|
141
187
|
if (event.type === "item.completed") {
|
|
142
188
|
const { item } = cliShaped(binary, itemCompleted, event.value);
|
|
143
189
|
// A turn also completes `reasoning`, `web_search`, `command_execution` and
|
|
@@ -156,22 +202,36 @@ export function codexLlm(deps) {
|
|
|
156
202
|
// ceiling: Codex reports no stop reason, so the continuation loop cannot tell a
|
|
157
203
|
// finished answer from one cut at the output limit. A `--output-schema` run
|
|
158
204
|
// would, at the cost of constraining every stage's answer.
|
|
159
|
-
|
|
205
|
+
const limits = await readingOf(before, deps.readLimits);
|
|
206
|
+
yield {
|
|
207
|
+
type: "done",
|
|
208
|
+
usage: usageOf(usage),
|
|
209
|
+
finishReason: null,
|
|
210
|
+
...(limits === undefined ? {} : { limits }),
|
|
211
|
+
};
|
|
160
212
|
return;
|
|
161
213
|
}
|
|
162
214
|
if (event.type === "turn.failed") {
|
|
163
|
-
const
|
|
215
|
+
const failed = cliShaped(binary, turnFailed, event.value).error.message;
|
|
216
|
+
const login = cliLoginError("codex", failed);
|
|
164
217
|
if (login)
|
|
165
218
|
throw login;
|
|
219
|
+
const planLimit = codexPlanLimit(failed, (deps.now ?? (() => new Date()))());
|
|
220
|
+
if (planLimit !== undefined)
|
|
221
|
+
throw codexLimitError(failed, planLimit);
|
|
166
222
|
throw providerError({
|
|
167
223
|
kind: "other",
|
|
168
224
|
message: cliReported(binary, redact(cliShaped(binary, turnFailed, event.value).error.message), cliCheck(binary)),
|
|
169
225
|
});
|
|
170
226
|
}
|
|
171
227
|
if (event.type === "error") {
|
|
172
|
-
const
|
|
228
|
+
const failed = cliShaped(binary, errorEvent, event.value).message;
|
|
229
|
+
const login = cliLoginError("codex", failed);
|
|
173
230
|
if (login)
|
|
174
231
|
throw login;
|
|
232
|
+
const planLimit = codexPlanLimit(failed, (deps.now ?? (() => new Date()))());
|
|
233
|
+
if (planLimit !== undefined)
|
|
234
|
+
throw codexLimitError(failed, planLimit);
|
|
175
235
|
throw providerError({
|
|
176
236
|
kind: "other",
|
|
177
237
|
message: cliReported(binary, redact(cliShaped(binary, errorEvent, event.value).message), cliCheck(binary)),
|
|
@@ -190,6 +250,7 @@ export function codexLlm(deps) {
|
|
|
190
250
|
}
|
|
191
251
|
finally {
|
|
192
252
|
documents?.remove();
|
|
253
|
+
images?.remove();
|
|
193
254
|
workspace.remove();
|
|
194
255
|
}
|
|
195
256
|
}
|
|
@@ -201,15 +262,18 @@ export function codexLlm(deps) {
|
|
|
201
262
|
const login = cliLoginError("codex", run.stderr());
|
|
202
263
|
if (login)
|
|
203
264
|
throw login;
|
|
265
|
+
const planLimit = codexPlanLimit(run.stderr(), (deps.now ?? (() => new Date()))());
|
|
266
|
+
if (planLimit !== undefined)
|
|
267
|
+
throw codexLimitError(run.stderr().trim(), planLimit);
|
|
204
268
|
throw providerError({
|
|
205
|
-
kind:
|
|
269
|
+
kind: endedKind(ended),
|
|
206
270
|
message: ended === undefined ? stuckCli(binary) : endedWithout(binary, ended, run.stderr()),
|
|
207
271
|
});
|
|
208
272
|
}
|
|
209
273
|
return {
|
|
210
274
|
id: "codex",
|
|
211
275
|
// Prose arrives as whole messages; other JSONL events carry activity separately.
|
|
212
|
-
capabilities: { streams: true, reportsUsage: true, webSearch: true },
|
|
276
|
+
capabilities: { streams: true, reportsUsage: true, webSearch: true, images: true },
|
|
213
277
|
models: deps.readModels ?? (() => nodeCodexModels(process.env, binary)),
|
|
214
278
|
complete,
|
|
215
279
|
};
|
|
@@ -238,5 +302,28 @@ function codexWorkspace(sourceEnv) {
|
|
|
238
302
|
function usageOf(usage) {
|
|
239
303
|
return usage === null || usage === undefined
|
|
240
304
|
? null
|
|
241
|
-
: {
|
|
305
|
+
: {
|
|
306
|
+
inputTokens: usage.input_tokens,
|
|
307
|
+
outputTokens: usage.output_tokens,
|
|
308
|
+
...(usage.cached_input_tokens ? { cachedInputTokens: usage.cached_input_tokens } : {}),
|
|
309
|
+
};
|
|
310
|
+
}
|
|
311
|
+
// The windows before (already asked) and after the call; undefined when neither arrived.
|
|
312
|
+
export async function readingOf(before, read) {
|
|
313
|
+
if (read === undefined)
|
|
314
|
+
return undefined;
|
|
315
|
+
const [start, end] = await Promise.all([before, read().catch(() => null)]);
|
|
316
|
+
if (!start?.length && !end?.length)
|
|
317
|
+
return undefined;
|
|
318
|
+
return {
|
|
319
|
+
...(start?.length ? { before: start } : {}),
|
|
320
|
+
...(end?.length ? { after: end } : {}),
|
|
321
|
+
};
|
|
322
|
+
}
|
|
323
|
+
export function codexLimitError(message, planLimit) {
|
|
324
|
+
return providerError({
|
|
325
|
+
kind: "rate_limit",
|
|
326
|
+
message: `Your Codex plan's usage limit is used up (the Codex CLI said: ${redact(message)}). Slopify waits for it to reset and then carries on by itself.`,
|
|
327
|
+
planLimit,
|
|
328
|
+
});
|
|
242
329
|
}
|
|
@@ -16,7 +16,7 @@ export function documentWorkspace(input) {
|
|
|
16
16
|
if (!parsed.success)
|
|
17
17
|
throw providerError({
|
|
18
18
|
kind: "unsupported",
|
|
19
|
-
message: "The research notes are too large, or not in a form the AI model can be given, so nothing was sent. Make the inputs shorter in Edit project, then use
|
|
19
|
+
message: "The research notes are too large, or not in a form the AI model can be given, so nothing was sent. Make the inputs shorter in Edit project, then use Try again; if it keeps happening, use Download diagnostics in Settings and report it.",
|
|
20
20
|
});
|
|
21
21
|
const documents = parsed.data;
|
|
22
22
|
const directory = mkdtempSync(join(tmpdir(), "slopify-documents-"));
|
|
@@ -82,6 +82,6 @@ export function documentWorkspace(input) {
|
|
|
82
82
|
function unread() {
|
|
83
83
|
return providerError({
|
|
84
84
|
kind: "unavailable",
|
|
85
|
-
message: "The AI model did not read all of the research notes, so its answer was not used (the attempt may still have used your quota). Use
|
|
85
|
+
message: "The AI model did not read all of the research notes, so its answer was not used (the attempt may still have used your quota). Use Try again; if it keeps happening, choose another model in the Providers section of Edit project.",
|
|
86
86
|
});
|
|
87
87
|
}
|
|
@@ -33,7 +33,7 @@ export function geminiWorkspace(webSearch, request, documents, env = process.env
|
|
|
33
33
|
catch {
|
|
34
34
|
throw providerError({
|
|
35
35
|
kind: "unavailable",
|
|
36
|
-
message: "Slopify could not read the Gemini CLI's settings file (settings.json in the .gemini folder of your home directory), which says how it signs in. Fix or remove that file, run gemini in a terminal to sign in again, then use
|
|
36
|
+
message: "Slopify could not read the Gemini CLI's settings file (settings.json in the .gemini folder of your home directory), which says how it signs in. Fix or remove that file, run gemini in a terminal to sign in again, then use Try again.",
|
|
37
37
|
});
|
|
38
38
|
}
|
|
39
39
|
const oauth = join(originalState, "oauth_creds.json");
|
|
@@ -5,6 +5,7 @@ import { cliCheck, cliName, cliReported } from "../explain.js";
|
|
|
5
5
|
import { documentWorkspace } from "./document-workspace.js";
|
|
6
6
|
import { nodeGeminiModels } from "./gemini-models.js";
|
|
7
7
|
import { geminiWorkspace } from "./gemini-workspace.js";
|
|
8
|
+
import { noImages } from "./image-workspace.js";
|
|
8
9
|
import { cliEvent, cliInput, cliShaped, deliveredInput, endedWithout, promptOf, stopCliRun, stuckCli, } from "./run-cli.js";
|
|
9
10
|
import { lines } from "./sse-lines.js";
|
|
10
11
|
export const geminiBinary = "gemini";
|
|
@@ -42,6 +43,9 @@ export function geminiLlm(deps) {
|
|
|
42
43
|
const binary = deps.binary ?? geminiBinary;
|
|
43
44
|
async function* complete(req) {
|
|
44
45
|
req.signal.throwIfAborted();
|
|
46
|
+
// Its CLI has no image flag, and `@file` inclusion would need a file tool this adapter keeps off.
|
|
47
|
+
if (req.images?.length)
|
|
48
|
+
throw noImages("the Gemini CLI");
|
|
45
49
|
const documents = documentWorkspace(req.documents);
|
|
46
50
|
let workspace;
|
|
47
51
|
let run;
|
|
@@ -104,7 +108,7 @@ export function geminiLlm(deps) {
|
|
|
104
108
|
}
|
|
105
109
|
req.signal.throwIfAborted();
|
|
106
110
|
if (run === undefined)
|
|
107
|
-
throw failure(binary, "", `The ${cliName(binary)} could not be started. Check it is installed and set up in Settings → Providers, then use
|
|
111
|
+
throw failure(binary, "", `The ${cliName(binary)} could not be started. Check it is installed and set up in Settings → Providers, then use Try again.`);
|
|
108
112
|
if (authRequired(run.stderr()))
|
|
109
113
|
throw authFailure();
|
|
110
114
|
throw failure(binary, run.stderr(), ended === undefined ? stuckCli(binary) : endedWithout(binary, ended, run.stderr()));
|
|
@@ -122,7 +126,16 @@ function failure(binary, message, sentence) {
|
|
|
122
126
|
if (/(?:#?3501\b|do not have a valid licen[cs]e of this product)/i.test(message)) {
|
|
123
127
|
return providerError({
|
|
124
128
|
kind: "unsupported",
|
|
125
|
-
message: "Google refused the Gemini CLI because the signed-in account has no licence for it (#3501). Update the Gemini CLI, run gemini in a terminal to sign in again, then use
|
|
129
|
+
message: "Google refused the Gemini CLI because the signed-in account has no licence for it (#3501). Update the Gemini CLI, run gemini in a terminal to sign in again, then use Try again; for a work or school account, ask your administrator for a Gemini licence.",
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
// The daily allowance of a signed-in Gemini plan. The CLI names no reset time, so the wait
|
|
133
|
+
// checks again later; a per-minute quota is an ordinary rate limit and is retried as one.
|
|
134
|
+
if (/(?:daily|per.day).{0,40}quota|quota.{0,40}(?:daily|per.day)/i.test(message)) {
|
|
135
|
+
return providerError({
|
|
136
|
+
kind: "rate_limit",
|
|
137
|
+
message: `Your Gemini plan's daily quota is used up (the Gemini CLI said: ${redact(message)}). Slopify waits and tries again later by itself.`,
|
|
138
|
+
planLimit: { account: "gemini", resetsAt: null },
|
|
126
139
|
});
|
|
127
140
|
}
|
|
128
141
|
return authRequired(message)
|
|
@@ -138,7 +151,7 @@ function authRequired(message) {
|
|
|
138
151
|
function authFailure() {
|
|
139
152
|
return providerError({
|
|
140
153
|
kind: "missing_key",
|
|
141
|
-
message: 'The Gemini CLI is not signed in, or its sign-in has expired. Open a terminal on the computer running the CLI, run "gemini" and sign in, then use
|
|
154
|
+
message: 'The Gemini CLI is not signed in, or its sign-in has expired. Open a terminal on the computer running the CLI, run "gemini" and sign in, then use Try again.',
|
|
142
155
|
});
|
|
143
156
|
}
|
|
144
157
|
async function* authChecked(source) {
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { copyFileSync, mkdtempSync, rmSync } from "node:fs";
|
|
2
|
+
import { tmpdir } from "node:os";
|
|
3
|
+
import { basename, extname, join } from "node:path";
|
|
4
|
+
import { providerError } from "../../kernel/ports/model.js";
|
|
5
|
+
const extensions = new Set([".png", ".jpg", ".jpeg", ".webp"]);
|
|
6
|
+
// ceiling: a review shows one image, or a short's handful plus the establishing image.
|
|
7
|
+
export const imagesMax = 12;
|
|
8
|
+
// The pictures a CLI model is asked to look at, copied into a private folder of their own so
|
|
9
|
+
// the CLI's file tool can be confined to it: nothing else on the disk is in reach.
|
|
10
|
+
export function imageWorkspace(input) {
|
|
11
|
+
if (!input?.length)
|
|
12
|
+
return undefined;
|
|
13
|
+
if (input.length > imagesMax ||
|
|
14
|
+
input.some((image) => !extensions.has(extname(image.path).toLowerCase())))
|
|
15
|
+
throw providerError({
|
|
16
|
+
kind: "unsupported",
|
|
17
|
+
message: "Slopify could not hand these pictures to the reviewer (too many, or not PNG, JPEG or WebP files), so nothing was sent. Use Try again; if it happens again, use Download diagnostics in Settings and report it.",
|
|
18
|
+
});
|
|
19
|
+
const directory = mkdtempSync(join(tmpdir(), "slopify-images-"));
|
|
20
|
+
const remove = () => rmSync(directory, { recursive: true, force: true, maxRetries: 3 });
|
|
21
|
+
try {
|
|
22
|
+
const files = input.map((image, index) => {
|
|
23
|
+
const file = `image-${String(index + 1)}${extname(image.path).toLowerCase()}`;
|
|
24
|
+
copyFileSync(image.path, join(directory, file));
|
|
25
|
+
return { file, path: join(directory, file), name: image.name };
|
|
26
|
+
});
|
|
27
|
+
return {
|
|
28
|
+
directory,
|
|
29
|
+
files,
|
|
30
|
+
remove,
|
|
31
|
+
instructions: `Open and look at every one of these image files with the Read tool before you answer; they are in the current folder and are the only files you may open:\n${files.map((one) => `- ${one.file}: ${one.name}`).join("\n")}`,
|
|
32
|
+
// Which files a Read call named, by file name: the CLI may report a relative or an
|
|
33
|
+
// absolute path.
|
|
34
|
+
unread(read) {
|
|
35
|
+
return files.filter((one) => !read.has(one.file)).map((one) => one.file);
|
|
36
|
+
},
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
catch (error) {
|
|
40
|
+
remove();
|
|
41
|
+
throw error;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
export function readFileName(path) {
|
|
45
|
+
return typeof path === "string" && path !== "" ? basename(path) : undefined;
|
|
46
|
+
}
|
|
47
|
+
export function unseenImages() {
|
|
48
|
+
return providerError({
|
|
49
|
+
kind: "unavailable",
|
|
50
|
+
message: "The reviewer answered without opening every picture it was given, so its verdict was not used (the attempt may still have used your quota). Use Try again; if it keeps happening, choose Codex as the reviewer in Edit project → Reviews.",
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
export function noImages(provider) {
|
|
54
|
+
return providerError({
|
|
55
|
+
kind: "unsupported",
|
|
56
|
+
message: `Slopify can't show pictures to ${provider}, so a review that needs them was not sent. In Edit project → Reviews, choose Claude Code or Codex as the reviewer, or set the picture reviews to Off, then use Try again.`,
|
|
57
|
+
});
|
|
58
|
+
}
|
|
@@ -4,6 +4,7 @@ import { documentMessages } from "../../kernel/ports/llm-documents.js";
|
|
|
4
4
|
import { providerError } from "../../kernel/ports/model.js";
|
|
5
5
|
import { droppedStream, httpFailure, missingKey, providerSaid, refreshList, unreadable, } from "../explain.js";
|
|
6
6
|
import { retryAfter } from "../retry-after.js";
|
|
7
|
+
import { noImages } from "./image-workspace.js";
|
|
7
8
|
import { sseData } from "./sse-lines.js";
|
|
8
9
|
// The HTTP gateway adapter: the platform's own `fetch` plus the line reader beside this
|
|
9
10
|
// file, no SDK. Neither is worth a dependency.
|
|
@@ -39,6 +40,10 @@ const streamChunk = z.object({
|
|
|
39
40
|
});
|
|
40
41
|
export function openRouterLlm(deps) {
|
|
41
42
|
async function* complete(req) {
|
|
43
|
+
// ceiling: text only. The catalogue does not yet say which models take images, and an older
|
|
44
|
+
// Slopify parses the fetched catalogue strictly, so a new key there would break it.
|
|
45
|
+
if (req.images?.length)
|
|
46
|
+
throw noImages("OpenRouter models");
|
|
42
47
|
const response = await deps.fetch(`${openRouterBase}/chat/completions`, {
|
|
43
48
|
method: "POST",
|
|
44
49
|
signal: req.signal,
|
|
@@ -63,7 +68,7 @@ export function openRouterLlm(deps) {
|
|
|
63
68
|
throw await failure(response);
|
|
64
69
|
}
|
|
65
70
|
if (response.body === null) {
|
|
66
|
-
throw providerError({ kind: "
|
|
71
|
+
throw providerError({ kind: "dropped", message: droppedStream("OpenRouter") });
|
|
67
72
|
}
|
|
68
73
|
let usage = null;
|
|
69
74
|
let finishReason = null;
|
|
@@ -108,7 +113,7 @@ export function openRouterLlm(deps) {
|
|
|
108
113
|
// Neither `[DONE]` nor the usage frame arrived, so the connection dropped part-way.
|
|
109
114
|
// Saying so is what stops a truncated answer being stored as a whole one.
|
|
110
115
|
throw providerError({
|
|
111
|
-
kind: "
|
|
116
|
+
kind: "dropped",
|
|
112
117
|
message: droppedStream("OpenRouter"),
|
|
113
118
|
});
|
|
114
119
|
}
|
|
@@ -162,6 +167,9 @@ function kindOf(status) {
|
|
|
162
167
|
// ceiling: everything else is `other` and is retried, so a 400 or a 402 fails the same
|
|
163
168
|
// way four times over. A terminal "this will never work" kind has to reach the port's
|
|
164
169
|
// error contract first, which is not this adapter's to widen.
|
|
170
|
+
// The provider's own server failed; the same request may well succeed later.
|
|
171
|
+
if (status >= 500)
|
|
172
|
+
return "dropped";
|
|
165
173
|
return "other";
|
|
166
174
|
}
|
|
167
175
|
async function failure(response, next) {
|
|
@@ -185,7 +193,7 @@ async function failure(response, next) {
|
|
|
185
193
|
function streamFailure(status, detail) {
|
|
186
194
|
return status >= 400
|
|
187
195
|
? httpFailure({ provider: "OpenRouter", status, detail })
|
|
188
|
-
: providerSaid("OpenRouter", "stopped part-way through the answer", detail, "Use
|
|
196
|
+
: providerSaid("OpenRouter", "stopped part-way through the answer", detail, "Use Try again; if it keeps happening, choose another model in the Providers section of Edit project.");
|
|
189
197
|
}
|
|
190
198
|
function statusOf(code) {
|
|
191
199
|
return typeof code === "number" ? code : Number(code ?? 0);
|
|
@@ -130,7 +130,7 @@ export async function deliveredInput(run) {
|
|
|
130
130
|
catch {
|
|
131
131
|
throw providerError({
|
|
132
132
|
kind: "unavailable",
|
|
133
|
-
message: "Slopify could not hand the whole prompt to the CLI, so it cannot tell what the CLI worked on (it may already have used your quota). Use
|
|
133
|
+
message: "Slopify could not hand the whole prompt to the CLI, so it cannot tell what the CLI worked on (it may already have used your quota). Use Try again to run it again.",
|
|
134
134
|
});
|
|
135
135
|
}
|
|
136
136
|
}
|
|
@@ -139,7 +139,7 @@ export function cliInput(text) {
|
|
|
139
139
|
if (Buffer.byteLength(text) >= 8 * 1024 * 1024)
|
|
140
140
|
throw providerError({
|
|
141
141
|
kind: "unsupported",
|
|
142
|
-
message: "This prompt is larger than the CLI accepts (8 MB), so nothing was sent. Make the inputs shorter in Edit project, or choose an API provider such as OpenRouter in its Providers section, then use
|
|
142
|
+
message: "This prompt is larger than the CLI accepts (8 MB), so nothing was sent. Make the inputs shorter in Edit project, or choose an API provider such as OpenRouter in its Providers section, then use Try again.",
|
|
143
143
|
});
|
|
144
144
|
return text;
|
|
145
145
|
}
|
|
@@ -168,13 +168,18 @@ export function promptOf(messages) {
|
|
|
168
168
|
export function endedWithout(binary, ended, stderr) {
|
|
169
169
|
const said = redact(stderr.trim());
|
|
170
170
|
if (ended.error !== null)
|
|
171
|
-
return `The ${cliName(binary)} could not be started${quoted("", redact(ended.error.message))}. Check it is installed and set up in Settings → Providers, then use
|
|
171
|
+
return `The ${cliName(binary)} could not be started${quoted("", redact(ended.error.message))}. Check it is installed and set up in Settings → Providers, then use Try again.`;
|
|
172
172
|
const how = ended.code === null ? "killed by the system" : `exit code ${String(ended.code)}`;
|
|
173
|
-
return `The ${cliName(binary)} stopped without answering${quoted(how, said)}. Run ${commandOf(binary)} in a terminal to check it works and is signed in, then use
|
|
173
|
+
return `The ${cliName(binary)} stopped without answering${quoted(how, said)}. Run ${commandOf(binary)} in a terminal to check it works and is signed in, then use Try again.`;
|
|
174
|
+
}
|
|
175
|
+
// A CLI the system killed mid-answer (out of memory, a sleep, a closed session) dropped the
|
|
176
|
+
// call; one that exited with an error of its own said something about the request.
|
|
177
|
+
export function endedKind(ended) {
|
|
178
|
+
return ended !== undefined && ended.error === null && ended.code === null ? "dropped" : "other";
|
|
174
179
|
}
|
|
175
180
|
// The run outlived both the polite and the forced stop.
|
|
176
181
|
export function stuckCli(binary) {
|
|
177
|
-
return `The ${cliName(binary)} stopped responding and could not be shut down. Close any leftover ${commandOf(binary)} processes or restart your computer, then use
|
|
182
|
+
return `The ${cliName(binary)} stopped responding and could not be shut down. Close any leftover ${commandOf(binary)} processes or restart your computer, then use Try again.`;
|
|
178
183
|
}
|
|
179
184
|
// Unreachable while stdio names `pipe` for fd 1; it is here so the type carries no null
|
|
180
185
|
// and no non-null assertion is needed to read the stream.
|
|
@@ -195,7 +200,7 @@ export function cliEvent(binary, line) {
|
|
|
195
200
|
// know. The text is not echoed back: half a JSON object is noise to the user.
|
|
196
201
|
throw providerError({
|
|
197
202
|
kind: "other",
|
|
198
|
-
message: `The ${cliName(binary)} sent output Slopify could not read. Update it to the latest version, then use
|
|
203
|
+
message: `The ${cliName(binary)} sent output Slopify could not read. Update it to the latest version, then use Try again.`,
|
|
199
204
|
});
|
|
200
205
|
}
|
|
201
206
|
return { type: parsed.data.type, value };
|
|
@@ -205,7 +210,7 @@ export function cliShaped(binary, schema, value) {
|
|
|
205
210
|
if (!parsed.success) {
|
|
206
211
|
throw providerError({
|
|
207
212
|
kind: "other",
|
|
208
|
-
message: `The ${cliName(binary)} sent output Slopify could not read. Update it to the latest version, then use
|
|
213
|
+
message: `The ${cliName(binary)} sent output Slopify could not read. Update it to the latest version, then use Try again.`,
|
|
209
214
|
});
|
|
210
215
|
}
|
|
211
216
|
return parsed.data;
|
|
@@ -3,6 +3,7 @@ import { redact } from "../../kernel/log.js";
|
|
|
3
3
|
import { providerError } from "../../kernel/ports/model.js";
|
|
4
4
|
import { httpFailure, missingKey, noAudio, voiceFix } from "../explain.js";
|
|
5
5
|
import { retryAfter } from "../retry-after.js";
|
|
6
|
+
import { cartesiaLanguages, lookUp } from "./voice-languages.js";
|
|
6
7
|
// The HTTP gateway adapter for Cartesia. `fetch` and
|
|
7
8
|
// nothing else; `/tts/bytes` streams the audio it renders, so the response body is
|
|
8
9
|
// already the stream the port asks for.
|
|
@@ -32,6 +33,12 @@ export function cartesiaTts(deps) {
|
|
|
32
33
|
return {
|
|
33
34
|
id: "cartesia",
|
|
34
35
|
capabilities: { streams: true },
|
|
36
|
+
voiceLanguages: async (voiceId, signal) => {
|
|
37
|
+
const key = deps.key();
|
|
38
|
+
if (key === undefined || key === "")
|
|
39
|
+
return undefined;
|
|
40
|
+
return cartesiaLanguages(await lookUp(deps.fetch, `${cartesiaBase}/voices/${encodeURIComponent(voiceId)}`, { "X-API-Key": key, "Cartesia-Version": cartesiaVersion }, signal));
|
|
41
|
+
},
|
|
35
42
|
models: async () => cartesiaModels,
|
|
36
43
|
synthesize: async (req) => {
|
|
37
44
|
const response = await deps.fetch(`${cartesiaBase}/tts/bytes`, {
|
|
@@ -79,6 +86,9 @@ function kindOf(status) {
|
|
|
79
86
|
return "rate_limit";
|
|
80
87
|
}
|
|
81
88
|
// ceiling: as in the other two adapters, everything else is `other` and is retried.
|
|
89
|
+
// The provider's own server failed; the same request may well succeed later.
|
|
90
|
+
if (status >= 500)
|
|
91
|
+
return "dropped";
|
|
82
92
|
return "other";
|
|
83
93
|
}
|
|
84
94
|
async function failure(response, voiceId) {
|
|
@@ -3,6 +3,7 @@ import { redact } from "../../kernel/log.js";
|
|
|
3
3
|
import { providerError } from "../../kernel/ports/model.js";
|
|
4
4
|
import { httpFailure, missingKey, noAudio, refreshList, unreadable, voiceFix } from "../explain.js";
|
|
5
5
|
import { retryAfter } from "../retry-after.js";
|
|
6
|
+
import { elevenLabsLanguages, lookUp } from "./voice-languages.js";
|
|
6
7
|
// The HTTP gateway adapter for ElevenLabs: the platform's own `fetch` and nothing else. The
|
|
7
8
|
// response body already is the `ReadableStream<Uint8Array>` the port asks for, so an SDK
|
|
8
9
|
// would buy nothing but a dependency.
|
|
@@ -19,6 +20,7 @@ export const elevenLabsModels = [
|
|
|
19
20
|
{ id: "eleven_turbo_v2_5", name: "Eleven Turbo v2.5 (deprecated)" },
|
|
20
21
|
{ id: "eleven_turbo_v2", name: "Eleven Turbo v2 (deprecated)" },
|
|
21
22
|
];
|
|
23
|
+
export const elevenLabsDialogueModel = "eleven_v3";
|
|
22
24
|
// mp3 at the port's container, 44.1 kHz, 128 kbps: `kernel/ports/tts.ts` fixes mp3 and the
|
|
23
25
|
// concatenation keeps the provider's own sample rate.
|
|
24
26
|
export const elevenLabsFormat = "mp3_44100_128";
|
|
@@ -38,7 +40,13 @@ const errorBody = z.object({
|
|
|
38
40
|
export function elevenLabsTts(deps) {
|
|
39
41
|
return {
|
|
40
42
|
id: "elevenlabs",
|
|
41
|
-
capabilities: { streams: true },
|
|
43
|
+
capabilities: { streams: true, dialogue: true },
|
|
44
|
+
voiceLanguages: async (voiceId, signal) => {
|
|
45
|
+
const key = deps.key();
|
|
46
|
+
if (key === undefined || key === "")
|
|
47
|
+
return undefined;
|
|
48
|
+
return elevenLabsLanguages(await lookUp(deps.fetch, `${elevenLabsBase}/voices/${encodeURIComponent(voiceId)}`, { "xi-api-key": key }, signal));
|
|
49
|
+
},
|
|
42
50
|
models: async () => {
|
|
43
51
|
const response = await deps.fetch(`${elevenLabsBase}/models`, {
|
|
44
52
|
headers: { "xi-api-key": keyOf(deps) },
|
|
@@ -59,14 +67,25 @@ export function elevenLabsTts(deps) {
|
|
|
59
67
|
},
|
|
60
68
|
synthesize: async (req) => {
|
|
61
69
|
const voice = encodeURIComponent(req.voiceId);
|
|
62
|
-
const response =
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
+
const response = req.dialogue === undefined
|
|
71
|
+
? await deps.fetch(`${elevenLabsBase}/text-to-speech/${voice}/stream?output_format=${elevenLabsFormat}`, {
|
|
72
|
+
method: "POST",
|
|
73
|
+
signal: req.signal,
|
|
74
|
+
headers: { "xi-api-key": keyOf(deps), "Content-Type": "application/json" },
|
|
75
|
+
// No pre-check on length. A text past the model's limit comes
|
|
76
|
+
// back as the provider's own 400 and that is what the stage shows.
|
|
77
|
+
body: JSON.stringify({ text: req.text, model_id: req.model ?? elevenLabsModel }),
|
|
78
|
+
})
|
|
79
|
+
: // Text to Dialogue: several voices in one conversation, on eleven_v3.
|
|
80
|
+
await deps.fetch(`${elevenLabsBase}/text-to-dialogue/stream?output_format=${elevenLabsFormat}`, {
|
|
81
|
+
method: "POST",
|
|
82
|
+
signal: req.signal,
|
|
83
|
+
headers: { "xi-api-key": keyOf(deps), "Content-Type": "application/json" },
|
|
84
|
+
body: JSON.stringify({
|
|
85
|
+
inputs: req.dialogue.map((line) => ({ text: line.text, voice_id: line.voiceId })),
|
|
86
|
+
model_id: req.model ?? elevenLabsDialogueModel,
|
|
87
|
+
}),
|
|
88
|
+
});
|
|
70
89
|
if (!response.ok) {
|
|
71
90
|
throw await failure(response, req.voiceId);
|
|
72
91
|
}
|
|
@@ -96,6 +115,9 @@ function kindOf(status) {
|
|
|
96
115
|
// ceiling: everything else is `other` and is retried, so a text past the character limit
|
|
97
116
|
// fails the same way four times over. A terminal "this will never work" kind has to reach the
|
|
98
117
|
// port's error contract first, which is not this adapter's to widen.
|
|
118
|
+
// The provider's own server failed; the same request may well succeed later.
|
|
119
|
+
if (status >= 500)
|
|
120
|
+
return "dropped";
|
|
99
121
|
return "other";
|
|
100
122
|
}
|
|
101
123
|
async function failure(response, voiceId, next) {
|