@gentbajko/slopify 4.0.0 → 4.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/adapters/account-limits.js +1 -1
- package/dist/adapters/alignment/audio.js +1 -1
- package/dist/adapters/alignment/cache.js +1 -1
- package/dist/adapters/alignment/multilingual.js +2 -1
- package/dist/adapters/alignment/runner.js +1 -1
- package/dist/adapters/alignment/worker.js +1 -1
- package/dist/adapters/explain.js +10 -9
- package/dist/adapters/host-cli/index.js +2 -1
- package/dist/adapters/host-cli/transport.js +2 -1
- package/dist/adapters/image/bytes.js +2 -1
- package/dist/adapters/image/codex.js +6 -5
- package/dist/adapters/image/fal.js +4 -3
- package/dist/adapters/image/google.js +3 -2
- package/dist/adapters/image/models.js +2 -2
- package/dist/adapters/image/openai.js +2 -1
- package/dist/adapters/image/replicate.js +3 -2
- package/dist/adapters/image/video.js +2 -1
- package/dist/adapters/key-probes.js +1 -1
- package/dist/adapters/llm/antigravity-models.js +1 -1
- package/dist/adapters/llm/antigravity.js +7 -6
- package/dist/adapters/llm/assistant-cli.js +1 -1
- package/dist/adapters/llm/claude-code-models.js +1 -1
- package/dist/adapters/llm/claude-code.js +3 -2
- package/dist/adapters/llm/codex-models.js +1 -1
- package/dist/adapters/llm/document-workspace.js +3 -2
- package/dist/adapters/llm/image-workspace.js +3 -2
- package/dist/adapters/llm/openrouter.js +2 -1
- package/dist/adapters/llm/run-cli.js +3 -2
- package/dist/adapters/tts/gemini.js +2 -1
- package/dist/adapters/tts/inworld-async.js +2 -1
- package/dist/adapters/tts/inworld.js +3 -2
- package/dist/adapters/tts/system.js +2 -1
- package/dist/catalog/registry.js +20 -13
- package/dist/catalog/schema.js +2 -1
- package/dist/catalog/validate.js +5 -2
- package/dist/edge/http/actions.js +7 -7
- package/dist/edge/http/app.js +3 -3
- package/dist/edge/http/assistant.js +3 -1
- package/dist/edge/http/backups.js +1 -1
- package/dist/edge/http/channel-memory.js +2 -2
- package/dist/edge/http/channels.js +1 -1
- package/dist/edge/http/checkpoints.js +7 -5
- package/dist/edge/http/design-templates.js +3 -3
- package/dist/edge/http/designs.js +1 -1
- package/dist/edge/http/document-themes.js +3 -3
- package/dist/edge/http/draft-problem.js +1 -1
- package/dist/edge/http/extension-auth.js +1 -1
- package/dist/edge/http/home.js +9 -1
- package/dist/edge/http/model-checks.js +4 -4
- package/dist/edge/http/onboarding.js +29 -12
- package/dist/edge/http/outputs.js +3 -2
- package/dist/edge/http/patch-notes.js +2 -2
- package/dist/edge/http/planning.js +1 -1
- package/dist/edge/http/project-create.js +1 -1
- package/dist/edge/http/project-templates.js +2 -2
- package/dist/edge/http/projects.js +22 -4
- package/dist/edge/http/prompts.js +2 -2
- package/dist/edge/http/providers.js +9 -10
- package/dist/edge/http/refusal.js +1 -1
- package/dist/edge/http/remote-access.js +4 -4
- package/dist/edge/http/remote-guard.js +2 -2
- package/dist/edge/http/reviews.js +1 -1
- package/dist/edge/http/revision-files.js +1 -1
- package/dist/edge/http/revisions.js +4 -4
- package/dist/edge/http/schedules.js +13 -11
- package/dist/edge/http/settings.js +6 -2
- package/dist/edge/http/social.js +5 -4
- package/dist/edge/http/staging.js +1 -1
- package/dist/edge/http/storage.js +12 -4
- package/dist/edge/http/studio.js +71 -19
- package/dist/edge/http/style-preview.js +2 -2
- package/dist/edge/http/template-studio.js +12 -9
- package/dist/edge/http/trash.js +1 -1
- package/dist/edge/http/youtube-edits.js +1 -1
- package/dist/edge/mcp/excluded-routes.js +4 -3
- package/dist/edge/mcp/instructions.js +3 -3
- package/dist/edge/mcp/library-tools.js +8 -5
- package/dist/edge/mcp/remake-tools.js +7 -4
- package/dist/edge/mcp/server.js +1 -1
- package/dist/edge/mcp/tools-channels.js +9 -9
- package/dist/edge/mcp/tools-designs.js +9 -9
- package/dist/edge/mcp/tools-library.js +7 -7
- package/dist/edge/mcp/tools-planning.js +18 -18
- package/dist/edge/mcp/tools-projects.js +10 -10
- package/dist/edge/mcp/tools-publishing.js +9 -9
- package/dist/edge/mcp/tools-runs.js +9 -9
- package/dist/edge/mcp/tools-schedules.js +6 -6
- package/dist/edge/mcp/tools-setup.js +20 -14
- package/dist/edge/mcp/tools-social.js +5 -4
- package/dist/edge/mcp/tools-storage.js +3 -3
- package/dist/edge/mcp/tools-template-studio.js +18 -18
- package/dist/edge/remote-access/model.js +1 -1
- package/dist/extension/slopify-studio-chrome.zip +0 -0
- package/dist/extension/slopify-studio-firefox.zip +0 -0
- package/dist/kernel/db/migrations/0078-uploaded-from-links.sql +7 -0
- package/dist/kernel/pipeline.js +12 -0
- package/dist/kernel/ports/settings-places.js +31 -0
- package/dist/kernel/ports/system-speech.js +31 -3
- package/dist/kernel/runner/attempt.js +11 -3
- package/dist/kernel/runner/providers.js +3 -2
- package/dist/kernel/runner/standalone.js +2 -2
- package/dist/main.js +5 -3
- package/dist/model-catalog.js +20 -0
- package/dist/patch-notes/4.0.0.md +13 -13
- package/dist/patch-notes/4.1.0.md +42 -0
- package/dist/patch-notes/index.json +6 -0
- package/dist/slices/admission/listing-facts.js +95 -0
- package/dist/slices/admission/repo.js +9 -6
- package/dist/slices/admission/rules.js +7 -7
- package/dist/slices/admission/state-words.js +34 -5
- package/dist/slices/assistant/cost.js +1 -1
- package/dist/slices/assistant/model.js +10 -3
- package/dist/slices/assistant/turn.js +5 -3
- package/dist/slices/autopilot/warnings.js +1 -1
- package/dist/slices/backups/service.js +5 -5
- package/dist/slices/channels/cast-images.js +1 -1
- package/dist/slices/channels/schema.js +1 -1
- package/dist/slices/channels/videos.js +1 -1
- package/dist/slices/control/providers.js +2 -2
- package/dist/slices/designs/budget.js +1 -1
- package/dist/slices/designs/calls.js +2 -2
- package/dist/slices/designs/check-context.js +4 -4
- package/dist/slices/designs/fields.js +4 -4
- package/dist/slices/designs/gate.js +7 -5
- package/dist/slices/designs/render-input.js +1 -1
- package/dist/slices/designs/requests.js +1 -1
- package/dist/slices/designs/runner.js +1 -1
- package/dist/slices/designs/sample.js +2 -1
- package/dist/slices/designs/service.js +1 -1
- package/dist/slices/designs/template-checks.js +2 -2
- package/dist/slices/designs/template-make.js +3 -3
- package/dist/slices/designs/templates.js +3 -3
- package/dist/slices/document/fonts.js +1 -1
- package/dist/slices/document/model.js +7 -4
- package/dist/slices/document/render.js +2 -1
- package/dist/slices/document/theme-schema.js +1 -1
- package/dist/slices/document/theme.js +49 -3
- package/dist/slices/episodes/summarize.js +2 -2
- package/dist/slices/estimate/index.js +28 -22
- package/dist/slices/estimate/requests.js +4 -4
- package/dist/slices/fixes/rules.js +3 -1
- package/dist/slices/fonts/catalog.js +2 -1
- package/dist/slices/fonts/coverage.js +2 -1
- package/dist/slices/images/appearance.js +2 -1
- package/dist/slices/images/scenes.js +2 -1
- package/dist/slices/keywords/digest.js +2 -1
- package/dist/slices/keywords/rules.js +2 -2
- package/dist/slices/keywords/specs.js +5 -5
- package/dist/slices/keywords/values.js +1 -1
- package/dist/slices/library/transfer.js +3 -3
- package/dist/slices/loudness/loudnorm.js +2 -1
- package/dist/slices/loudness/model.js +3 -2
- package/dist/slices/loudness/sentence-level.js +2 -1
- package/dist/slices/model-checks/rules.js +13 -0
- package/dist/slices/model-upkeep/model.js +1 -1
- package/dist/slices/model-upkeep/switch.js +3 -3
- package/dist/slices/model-upkeep/usage.js +13 -2
- package/dist/slices/narration/breaths.js +2 -1
- package/dist/slices/narration/clean-pieces.js +2 -1
- package/dist/slices/narration/pauses-model.js +3 -2
- package/dist/slices/narration/pronunciation.js +3 -2
- package/dist/slices/notifications/subject.js +2 -2
- package/dist/slices/onboarding/copy.js +19 -2
- package/dist/slices/onboarding/first-run.js +13 -38
- package/dist/slices/onboarding/install.js +2 -1
- package/dist/slices/onboarding/model.js +26 -21
- package/dist/slices/onboarding/preferred-model.js +14 -0
- package/dist/slices/onboarding/quick-short.js +103 -64
- package/dist/slices/onboarding/sample.js +4 -1
- package/dist/slices/onboarding/state.js +7 -0
- package/dist/slices/onboarding/template.js +6 -5
- package/dist/slices/outputs/add.js +3 -2
- package/dist/slices/outputs/service.js +2 -2
- package/dist/slices/patch-notes/library.js +1 -1
- package/dist/slices/play-drafts/readiness.js +3 -3
- package/dist/slices/play-drafts/review-inputs.js +1 -1
- package/dist/slices/play-drafts/service.js +17 -7
- package/dist/slices/project-templates/next-chapter.js +2 -1
- package/dist/slices/project-templates/provider-defaults.js +22 -0
- package/dist/slices/project-templates/setup-sections.js +26 -0
- package/dist/slices/project-templates/setup.js +6 -1
- package/dist/slices/rebuild/preview-details.js +5 -5
- package/dist/slices/rebuild/recipe-preparation.js +1 -1
- package/dist/slices/rebuild/recipe-save.js +2 -2
- package/dist/slices/rebuild/recipe-validation.js +5 -4
- package/dist/slices/rebuild/recipe-voices.js +3 -2
- package/dist/slices/rebuild/recipe-work.js +4 -4
- package/dist/slices/rebuild/recovery.js +6 -6
- package/dist/slices/rebuild/remake-outdated.js +5 -5
- package/dist/slices/rebuild/review-redo.js +1 -1
- package/dist/slices/rebuild/runtime-animate.js +2 -2
- package/dist/slices/rebuild/runtime-article.js +4 -3
- package/dist/slices/rebuild/runtime-document.js +5 -4
- package/dist/slices/rebuild/runtime-export-bed.js +2 -1
- package/dist/slices/rebuild/runtime-export-edit.js +2 -1
- package/dist/slices/rebuild/runtime-export-inputs.js +5 -4
- package/dist/slices/rebuild/runtime-export-short.js +1 -1
- package/dist/slices/rebuild/runtime-export.js +4 -3
- package/dist/slices/rebuild/runtime-image.js +4 -3
- package/dist/slices/rebuild/runtime-local.js +3 -2
- package/dist/slices/rebuild/runtime-narration-text.js +3 -2
- package/dist/slices/rebuild/runtime-provider.js +7 -6
- package/dist/slices/rebuild/runtime-review.js +6 -5
- package/dist/slices/rebuild/runtime-shorts.js +6 -5
- package/dist/slices/rebuild/runtime-store.js +17 -2
- package/dist/slices/rebuild/runtime-subtitles.js +13 -12
- package/dist/slices/rebuild/runtime-voices.js +2 -1
- package/dist/slices/rebuild/runtime-youtube.js +3 -2
- package/dist/slices/rebuild/service-readiness.js +11 -3
- package/dist/slices/research/synthesis.js +3 -2
- package/dist/slices/reviews/external-model.js +1 -1
- package/dist/slices/reviews/holds.js +5 -4
- package/dist/slices/reviews/model.js +8 -5
- package/dist/slices/reviews/rules.js +3 -3
- package/dist/slices/reviews/verdict.js +2 -1
- package/dist/slices/revisions/image-plan.js +1 -1
- package/dist/slices/revisions/mutation-assets.js +2 -1
- package/dist/slices/revisions/mutations.js +3 -2
- package/dist/slices/schedules/agenda.js +7 -5
- package/dist/slices/schedules/design-check.js +4 -4
- package/dist/slices/schedules/design-run.js +3 -3
- package/dist/slices/schedules/repo.js +6 -5
- package/dist/slices/schedules/schema.js +3 -0
- package/dist/slices/schedules/topics.js +5 -5
- package/dist/slices/settings/first-run.js +6 -32
- package/dist/slices/settings/health.js +30 -15
- package/dist/slices/settings/key-test.js +1 -1
- package/dist/slices/settings/model.js +4 -2
- package/dist/slices/settings/playback.js +2 -2
- package/dist/slices/shorts/keep.js +2 -1
- package/dist/slices/shorts/pick.js +4 -3
- package/dist/slices/shorts/prompts.js +2 -1
- package/dist/slices/shorts/render.js +3 -2
- package/dist/slices/social/media.js +5 -4
- package/dist/slices/social/pack.js +1 -1
- package/dist/slices/social/post-prompt.js +2 -1
- package/dist/slices/social/reshape.js +2 -2
- package/dist/slices/social/serve.js +1 -1
- package/dist/slices/social/service.js +3 -2
- package/dist/slices/social/state.js +1 -1
- package/dist/slices/storage/backup-import.js +2 -2
- package/dist/slices/storage/portable.js +1 -1
- package/dist/slices/storage/trim.js +23 -13
- package/dist/slices/studio/calendar.js +3 -1
- package/dist/slices/studio/disclosure.js +4 -4
- package/dist/slices/studio/model.js +1 -1
- package/dist/slices/studio/pack.js +9 -8
- package/dist/slices/studio/plan.js +1 -1
- package/dist/slices/studio/settings.js +6 -6
- package/dist/slices/studio/videos.js +23 -4
- package/dist/slices/style-preview/render.js +3 -3
- package/dist/slices/style-preview/schema.js +1 -1
- package/dist/slices/subtitles/captions.js +1 -1
- package/dist/slices/template-studio/anatomy-view.js +46 -3
- package/dist/slices/template-studio/bakeoff.js +3 -3
- package/dist/slices/template-studio/budget.js +6 -3
- package/dist/slices/template-studio/cards.js +78 -0
- package/dist/slices/template-studio/checks.js +99 -50
- package/dist/slices/template-studio/for-all.js +5 -5
- package/dist/slices/template-studio/free-video.js +1 -1
- package/dist/slices/template-studio/free.js +5 -5
- package/dist/slices/template-studio/model.js +19 -15
- package/dist/slices/template-studio/pack-import.js +14 -14
- package/dist/slices/template-studio/pack.js +9 -7
- package/dist/slices/template-studio/paid.js +12 -5
- package/dist/slices/template-studio/quote.js +18 -11
- package/dist/slices/template-studio/ready.js +7 -7
- package/dist/slices/template-studio/runner.js +1 -1
- package/dist/slices/template-studio/save.js +12 -12
- package/dist/slices/template-studio/say-it-as.js +3 -3
- package/dist/slices/template-studio/schedule-gate.js +1 -1
- package/dist/slices/template-studio/schedule-price.js +3 -1
- package/dist/slices/template-studio/schedule-sample.js +5 -1
- package/dist/slices/template-studio/schema.js +25 -0
- package/dist/slices/template-studio/service.js +3 -1
- package/dist/slices/template-studio/status.js +26 -9
- package/dist/slices/template-studio/topic.js +3 -3
- package/dist/slices/thumbnail/by-llm.js +1 -1
- package/dist/slices/uploads/repo.js +37 -1
- package/dist/slices/uploads/rules.js +3 -2
- package/dist/slices/video/edit-list.js +2 -2
- package/dist/slices/video/edit-settings.js +1 -1
- package/dist/slices/video/encoding.js +14 -5
- package/dist/slices/video/ffmpeg.js +2 -2
- package/dist/slices/video/plan.js +2 -1
- package/dist/slices/video/slideshow.js +1 -1
- package/dist/slices/voices/attribution.js +3 -2
- package/dist/slices/voices/model.js +9 -8
- package/dist/slices/voices/script.js +2 -1
- package/dist/slices/youtube/answer.js +2 -1
- package/dist/slices/youtube/edits-repo.js +1 -1
- package/dist/tutorials/AI-CLIs.md +25 -25
- package/dist/tutorials/Agents-MCP.md +32 -32
- package/dist/tutorials/Assistant.md +5 -5
- package/dist/tutorials/Backups.md +7 -7
- package/dist/tutorials/Calendar.md +17 -25
- package/dist/tutorials/Cast-Library.md +19 -19
- package/dist/tutorials/Channels.md +40 -35
- package/dist/tutorials/Costs-and-Run-Cost.md +38 -40
- package/dist/tutorials/Create-Article.md +114 -0
- package/dist/tutorials/Create-Extras.md +112 -0
- package/dist/tutorials/Create-Images.md +165 -0
- package/dist/tutorials/Create-Narration.md +178 -0
- package/dist/tutorials/Create-Overview.md +244 -0
- package/dist/tutorials/Create-Video-and-Style.md +144 -0
- package/dist/tutorials/Docker.md +3 -3
- package/dist/tutorials/Document-Themes.md +32 -32
- package/dist/tutorials/Editing-a-Project.md +94 -94
- package/dist/tutorials/FAQ.md +18 -18
- package/dist/tutorials/First-Launch-and-Welcome.md +50 -64
- package/dist/tutorials/Home-and-Projects.md +101 -76
- package/dist/tutorials/Home.md +33 -33
- package/dist/tutorials/Install.md +13 -13
- package/dist/tutorials/Intros-and-Outros.md +33 -34
- package/dist/tutorials/Keyboard-Shortcuts-and-Command-Palette.md +62 -58
- package/dist/tutorials/Keywords-Slopify-Fills.md +21 -21
- package/dist/tutorials/Models.md +27 -27
- package/dist/tutorials/Multiple-Voices.md +41 -40
- package/dist/tutorials/Narration-Aliases-and-Glossary.md +14 -14
- package/dist/tutorials/Notifications.md +21 -17
- package/dist/tutorials/Other-Languages.md +15 -15
- package/dist/tutorials/PDF-Documents.md +38 -29
- package/dist/tutorials/Posting-to-Social-Sites.md +12 -12
- package/dist/tutorials/Project-Page.md +118 -120
- package/dist/tutorials/Prompts-and-Templates.md +84 -0
- package/dist/tutorials/Prompts.md +45 -45
- package/dist/tutorials/Providers-and-Keys.md +105 -98
- package/dist/tutorials/Publishing-to-YouTube.md +31 -31
- package/dist/tutorials/Recovery-and-Retries.md +31 -31
- package/dist/tutorials/Remote-Access.md +2 -2
- package/dist/tutorials/Reviews-and-Checkpoints.md +90 -93
- package/dist/tutorials/Schedules.md +27 -28
- package/dist/tutorials/Settings-Reference.md +182 -204
- package/dist/tutorials/Shorts.md +34 -33
- package/dist/tutorials/Start-at-Login.md +3 -3
- package/dist/tutorials/Studio-Extension.md +22 -24
- package/dist/tutorials/Template-Studio.md +45 -42
- package/dist/tutorials/Templates.md +50 -79
- package/dist/tutorials/Trash.md +4 -4
- package/dist/tutorials/Troubleshooting.md +44 -44
- package/dist/tutorials/Uninstalling-and-Moving.md +5 -5
- package/dist/tutorials/Updating-and-Patch-Notes.md +18 -18
- package/dist/tutorials/Video-Editing.md +34 -34
- package/dist/tutorials/Where-Your-Files-Live.md +10 -10
- package/dist/tutorials/YouTube-Description.md +26 -26
- package/dist/tutorials/Your-First-Short.md +35 -35
- package/dist/tutorials/_Sidebar.md +15 -15
- package/dist/web/assets/index-BNftKQrA.css +1 -0
- package/dist/web/assets/index-Cpa58woP.js +459 -0
- package/dist/web/assets/pdf-BKh8f7R6.js +56 -0
- package/dist/web/assets/pdf.worker.min-BmVo14Nb.mjs +28 -0
- package/dist/web/assets/pdf.worker.min-D7i8o3wR.js +1 -0
- package/dist/web/index.html +2 -2
- package/package.json +2 -2
- package/dist/tutorials/Library-Overview.md +0 -76
- package/dist/tutorials/Play-Images.md +0 -165
- package/dist/tutorials/Play-Narration.md +0 -167
- package/dist/tutorials/Play-Outputs.md +0 -109
- package/dist/tutorials/Play-Overview.md +0 -227
- package/dist/tutorials/Play-Title-and-Article.md +0 -136
- package/dist/tutorials/Play-Video-and-Style.md +0 -143
- package/dist/web/assets/index-CkAI_2E7.css +0 -1
- package/dist/web/assets/index-DcD0cdfo.js +0 -334
- package/dist/web/assets/pdf-TODRwF31.js +0 -54
- package/dist/web/assets/pdf.worker.min-Dswkl-cV.mjs +0 -29
- package/dist/web/assets/pdf.worker.min-zC6nfbxG.js +0 -1
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { statfs } from "node:fs/promises";
|
|
2
2
|
import { z } from "zod";
|
|
3
3
|
// The autopilot's hourly look at what may stop the schedules: the OpenRouter key's spending
|
|
4
|
-
// limit (`GET /api/v1/key`, the same read Settings →
|
|
4
|
+
// limit (`GET /api/v1/key`, the same read Settings → Keys & services' Test makes) and the free disk.
|
|
5
5
|
// Read-only: neither starts paid work.
|
|
6
6
|
const keyAnswer = z.object({
|
|
7
7
|
data: z.object({ limit: z.number().nullable(), limit_remaining: z.number().nullable() }).loose(),
|
|
@@ -33,7 +33,7 @@ export async function decodeAudio(ffmpeg, source, output, signal) {
|
|
|
33
33
|
child.on("close", (code) => {
|
|
34
34
|
signal.removeEventListener("abort", abort);
|
|
35
35
|
if (signal.aborted)
|
|
36
|
-
reject(new Error("
|
|
36
|
+
reject(new Error("Caption audio preparation was canceled."));
|
|
37
37
|
else if (code !== 0)
|
|
38
38
|
reject(new Error(`Could not prepare the narration for subtitles. ${errorText}`));
|
|
39
39
|
else
|
|
@@ -170,7 +170,7 @@ async function seedModel(input, seed, target) {
|
|
|
170
170
|
await rm(staging, { recursive: true, force: true });
|
|
171
171
|
}
|
|
172
172
|
}
|
|
173
|
-
const smaller = " To download less, pick a smaller model in Settings → Production defaults →
|
|
173
|
+
const smaller = " To download less, pick a smaller model in Settings → Production defaults → Caption timing model.";
|
|
174
174
|
function megabytes(bytes) {
|
|
175
175
|
return `${String(Math.round(bytes / 1_000_000))} MB`;
|
|
176
176
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { languageInfo } from "../../kernel/ports/languages.js";
|
|
2
|
+
import { editSettings } from "../../kernel/ports/settings-places.js";
|
|
2
3
|
import { englishSpec } from "./spec.js";
|
|
3
4
|
import { spellers, spokenNumber } from "./spell.js";
|
|
4
5
|
import { respoken, speechWords } from "./text.js";
|
|
@@ -239,7 +240,7 @@ function spell(text, alphabet, raw, language) {
|
|
|
239
240
|
}
|
|
240
241
|
const folded = fold(character);
|
|
241
242
|
if (folded === undefined)
|
|
242
|
-
throw new Error(`Word timing for ${languageInfo(language).name} reads Latin letters only, and "${raw}" has letters from another alphabet. Write that word in Latin letters in the article (
|
|
243
|
+
throw new Error(`Word timing for ${languageInfo(language).name} reads Latin letters only, and "${raw}" has letters from another alphabet. Write that word in Latin letters in the article (${editSettings.article}), then Try again.`);
|
|
243
244
|
result += folded;
|
|
244
245
|
}
|
|
245
246
|
return result.replace(/\s+/g, " ").trim();
|
|
@@ -24,7 +24,7 @@ export async function runAlignmentWorker(input, signal, onProgress, worker = new
|
|
|
24
24
|
signal.removeEventListener("abort", abort);
|
|
25
25
|
child.kill("SIGKILL");
|
|
26
26
|
};
|
|
27
|
-
const abort = () => finish(new Error("
|
|
27
|
+
const abort = () => finish(new Error("Caption timing was canceled."));
|
|
28
28
|
signal.addEventListener("abort", abort, { once: true });
|
|
29
29
|
child.stderr?.on("data", (data) => {
|
|
30
30
|
stderr = (stderr + data.toString("utf8")).slice(-2000);
|
|
@@ -168,7 +168,7 @@ async function openSession(modelPath) {
|
|
|
168
168
|
const reason = nativeError instanceof Error
|
|
169
169
|
? (nativeError.message.split("\n")[0] ?? "")
|
|
170
170
|
: String(nativeError);
|
|
171
|
-
throw new Error(`
|
|
171
|
+
throw new Error(`Caption timing could not start: neither of its speech engines loaded on this computer (${system}): ${reason}. Reinstall Slopify so npm installs the engine for this system, then try again; if it keeps failing, turn captions off for this project.`);
|
|
172
172
|
}
|
|
173
173
|
}
|
|
174
174
|
const session = await native.InferenceSession.create(modelPath, {
|
package/dist/adapters/explain.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { editSettings } from "../kernel/ports/settings-places.js";
|
|
1
2
|
// The sentences a failed provider call shows. Whatever an adapter throws becomes the stage's
|
|
2
3
|
// failure reason on the project page, read by someone who only wants the video, so each one
|
|
3
4
|
// says what failed, the likely cause and the next step, using the names on screen. The
|
|
@@ -6,19 +7,19 @@
|
|
|
6
7
|
// A provider's error body can be a whole HTML page; the first few hundred characters say why.
|
|
7
8
|
const detailMax = 300;
|
|
8
9
|
export function missingKey(provider) {
|
|
9
|
-
return `No ${provider} API key is saved. Add one in Settings →
|
|
10
|
+
return `No ${provider} API key is saved. Add one in Settings → Keys & services, then use Try again.`;
|
|
10
11
|
}
|
|
11
12
|
export const refreshList = "refresh the list";
|
|
12
|
-
export const checkChoice =
|
|
13
|
+
export const checkChoice = `Check the service and model chosen in ${editSettings.open} (under Written by in Article, in Narration or in Images), then use Try again.`;
|
|
13
14
|
export function httpFailure(failure) {
|
|
14
15
|
const { provider, status } = failure;
|
|
15
16
|
const said = quoted(failure.label ?? `error ${String(status)}`, failure.detail);
|
|
16
17
|
const next = failure.next ?? "use Try again";
|
|
17
18
|
if (status === 401) {
|
|
18
|
-
return `${provider} did not accept the API key${said}. The key may be wrong, expired or revoked: paste a current key in Settings →
|
|
19
|
+
return `${provider} did not accept the API key${said}. The key may be wrong, expired or revoked: paste a current key in Settings → Keys & services, then ${next}.`;
|
|
19
20
|
}
|
|
20
21
|
if (status === 403) {
|
|
21
|
-
return `${provider} refused access${said}. The API key may not be allowed to use this model, or the account needs attention: check your ${provider} account and the key in Settings →
|
|
22
|
+
return `${provider} refused access${said}. The API key may not be allowed to use this model, or the account needs attention: check your ${provider} account and the key in Settings → Keys & services, then ${next}.`;
|
|
22
23
|
}
|
|
23
24
|
if (status === 402) {
|
|
24
25
|
return `${provider} says the account is out of credits${said}. Add credits on the ${provider} website, then ${next}.`;
|
|
@@ -27,7 +28,7 @@ export function httpFailure(failure) {
|
|
|
27
28
|
return `${provider} is limiting requests${said}. Too many requests were sent at once or the plan's quota is used up: wait a few minutes or check your ${provider} usage and billing, then ${next}.`;
|
|
28
29
|
}
|
|
29
30
|
if (status >= 400 && status < 500) {
|
|
30
|
-
return `${provider} rejected the ${failure.subject ?? "request"}${said}. ${failure.fix ?? (failure.next === undefined ? checkChoice : `Check the key in Settings →
|
|
31
|
+
return `${provider} rejected the ${failure.subject ?? "request"}${said}. ${failure.fix ?? (failure.next === undefined ? checkChoice : `Check the key in Settings → Keys & services, then ${next}.`)}`;
|
|
31
32
|
}
|
|
32
33
|
if (status >= 500) {
|
|
33
34
|
return `${provider} had a problem on its side${said}. This is usually temporary: wait a few minutes, then ${next}.`;
|
|
@@ -48,15 +49,15 @@ export function noAudio(provider) {
|
|
|
48
49
|
return `${provider} finished without sending any audio. Use Try again; if it keeps happening, check the voice in Settings → Voices.`;
|
|
49
50
|
}
|
|
50
51
|
export function noImage(provider) {
|
|
51
|
-
return `${provider} finished without sending an image. Use Try again; if it keeps happening, reword the image prompt in
|
|
52
|
+
return `${provider} finished without sending an image. Use Try again; if it keeps happening, reword the image prompt in ${editSettings.images} or choose another image model there.`;
|
|
52
53
|
}
|
|
53
54
|
export function refusedImage(provider, detail) {
|
|
54
|
-
return `${provider} refused to make this image under its content rules${quoted("", detail)}. Reword the image prompt in
|
|
55
|
+
return `${provider} refused to make this image under its content rules${quoted("", detail)}. Reword the image prompt in ${editSettings.images}, or choose another image service there.`;
|
|
55
56
|
}
|
|
56
57
|
// A narration request the provider turned down is most often a voice that no longer exists
|
|
57
58
|
// or a part longer than the model takes.
|
|
58
59
|
export function voiceFix(voiceId) {
|
|
59
|
-
return `Check that voice "${voiceId}" still exists in Settings → Voices and that each narration part fits the model's length limit (Chunking, in
|
|
60
|
+
return `Check that voice "${voiceId}" still exists in Settings → Voices and that each narration part fits the model's length limit (Chunking, in ${editSettings.narration}), then use Try again.`;
|
|
60
61
|
}
|
|
61
62
|
const cliNames = {
|
|
62
63
|
claude: "Claude Code CLI",
|
|
@@ -79,7 +80,7 @@ export function cliReported(binary, detail, next) {
|
|
|
79
80
|
return `The ${cliName(binary)} reported an error${quoted("", detail)}. ${next}`;
|
|
80
81
|
}
|
|
81
82
|
export function cliCheck(binary) {
|
|
82
|
-
return `Use Try again; if it keeps failing, run ${commandOf(binary)} in a terminal to check it works and is signed in, or choose another model in
|
|
83
|
+
return `Use Try again; if it keeps failing, run ${commandOf(binary)} in a terminal to check it works and is signed in, or choose another model in ${editSettings.writer}.`;
|
|
83
84
|
}
|
|
84
85
|
// ` (error 401: "Unauthorized")`, ` (error 401)`, ` ("Unauthorized")`, or nothing.
|
|
85
86
|
export function quoted(label, detail) {
|
|
@@ -2,12 +2,13 @@ import { redact } from "../../kernel/log.js";
|
|
|
2
2
|
import { bridgeLimits, decodeHostImageReport, hostFaultSchema, hostFrameSchema, hostHealthSchema, hostImageReportHeader, hostImageSchema, hostLlmSchema, hostModelsSchema, hostOpenedSchema, hostOpenFolderSchema, hostStatusSchema, } from "../../kernel/ports/host-cli.js";
|
|
3
3
|
import { agentImageTimeoutMs } from "../../kernel/ports/image.js";
|
|
4
4
|
import { isProviderError, providerError, } from "../../kernel/ports/model.js";
|
|
5
|
+
import { editSettings } from "../../kernel/ports/settings-places.js";
|
|
5
6
|
import { sniffImage } from "../image/bytes.js";
|
|
6
7
|
import { claudePlanLimit } from "../llm/claude-code.js";
|
|
7
8
|
import { codexPlanLimit } from "../llm/codex-limits.js";
|
|
8
9
|
import { accessField } from "../model-access.js";
|
|
9
10
|
import { hostRequest, hostUnavailable, readHostBytes, } from "./transport.js";
|
|
10
|
-
const tooBigForHost =
|
|
11
|
+
const tooBigForHost = `Slopify could not send this request to the host helper because it is larger than, or shaped differently from, what the helper accepts. Make the article or its research notes shorter in ${editSettings.article}, then use Try again; if it happens again, use Download diagnostics in Settings and report it.`;
|
|
11
12
|
export function createHostCliClient(options) {
|
|
12
13
|
async function request(input) {
|
|
13
14
|
if (!options.directory)
|
|
@@ -5,6 +5,7 @@ import { join } from "node:path";
|
|
|
5
5
|
import { bridgeLimits, hostFramesHeader, hostFramesVersion } from "../../kernel/ports/host-cli.js";
|
|
6
6
|
import { agentImageTimeoutMs } from "../../kernel/ports/image.js";
|
|
7
7
|
import { providerError } from "../../kernel/ports/model.js";
|
|
8
|
+
import { editSettings } from "../../kernel/ports/settings-places.js";
|
|
8
9
|
export function hostUnavailable(submitted = false) {
|
|
9
10
|
return providerError({
|
|
10
11
|
kind: "unavailable",
|
|
@@ -34,7 +35,7 @@ export async function hostRequest(options) {
|
|
|
34
35
|
if (options.body && options.body.byteLength > bridgeLimits.request)
|
|
35
36
|
throw providerError({
|
|
36
37
|
kind: "unsupported",
|
|
37
|
-
message:
|
|
38
|
+
message: `This request is larger than the host helper accepts (16 MB), so nothing was sent. Make the article or its research notes shorter in ${editSettings.article}, or choose an API service such as OpenRouter under Written by there, then use Try again.`,
|
|
38
39
|
});
|
|
39
40
|
let token;
|
|
40
41
|
try {
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { providerError } from "../../kernel/ports/model.js";
|
|
2
|
+
import { editSettings } from "../../kernel/ports/settings-places.js";
|
|
2
3
|
// Two of the three image providers answer with a link rather than with bytes, so fetching
|
|
3
4
|
// that link is part of the provider call and belongs inside the attempt with it: a URL
|
|
4
5
|
// that 404s or hands back an HTML error page is a failed attempt, not a corrupt file on
|
|
@@ -48,7 +49,7 @@ export async function downloadImage(download) {
|
|
|
48
49
|
if (mime === undefined) {
|
|
49
50
|
throw providerError({
|
|
50
51
|
kind: "other",
|
|
51
|
-
message: `${download.provider} 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
|
|
52
|
+
message: `${download.provider} 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 ${editSettings.images}.`,
|
|
52
53
|
});
|
|
53
54
|
}
|
|
54
55
|
return { bytes, mime };
|
|
@@ -5,6 +5,7 @@ import { z } from "zod";
|
|
|
5
5
|
import { redact } from "../../kernel/log.js";
|
|
6
6
|
import { agentImageTimeoutMs, } from "../../kernel/ports/image.js";
|
|
7
7
|
import { providerError } from "../../kernel/ports/model.js";
|
|
8
|
+
import { editSettings } from "../../kernel/ports/settings-places.js";
|
|
8
9
|
import { cliReported, quoted, refusedImage } from "../explain.js";
|
|
9
10
|
import { cliLoginError } from "../llm/cli-login-error.js";
|
|
10
11
|
import { codexLimitError, readingOf } from "../llm/codex.js";
|
|
@@ -212,7 +213,7 @@ export function codexImage(deps) {
|
|
|
212
213
|
if (req.model.trim() === "" || req.model.startsWith("-"))
|
|
213
214
|
throw providerError({
|
|
214
215
|
kind: "unsupported",
|
|
215
|
-
message:
|
|
216
|
+
message: `No Codex model is chosen for images. Choose one (or Codex default) in ${editSettings.images}, then use Try again.`,
|
|
216
217
|
});
|
|
217
218
|
req.signal.throwIfAborted();
|
|
218
219
|
const directory = mkdtempSync(join(tmpdir(), "slopify-codex-image-"));
|
|
@@ -253,7 +254,7 @@ export function codexImage(deps) {
|
|
|
253
254
|
catch {
|
|
254
255
|
throw providerError({
|
|
255
256
|
kind: "unsupported",
|
|
256
|
-
message: "The Codex CLI could not be started. Check it is installed and set up in Settings →
|
|
257
|
+
message: "The Codex CLI could not be started. Check it is installed and set up in Settings → Keys & services, then use Try again.",
|
|
257
258
|
});
|
|
258
259
|
}
|
|
259
260
|
let completed = false;
|
|
@@ -337,7 +338,7 @@ export function codexImage(deps) {
|
|
|
337
338
|
if (ended.error !== null)
|
|
338
339
|
throw providerError({
|
|
339
340
|
kind: "unsupported",
|
|
340
|
-
message: "The Codex CLI could not be started. Check it is installed and set up in Settings →
|
|
341
|
+
message: "The Codex CLI could not be started. Check it is installed and set up in Settings → Keys & services, then use Try again.",
|
|
341
342
|
});
|
|
342
343
|
const stderrLimit = codexPlanLimit(run.stderr(), new Date());
|
|
343
344
|
if ((ended.code !== 0 || !completed) && stderrLimit !== undefined)
|
|
@@ -397,8 +398,8 @@ async function* bounded(source, maximum) {
|
|
|
397
398
|
yield chunk;
|
|
398
399
|
}
|
|
399
400
|
}
|
|
400
|
-
const imageNext =
|
|
401
|
-
const cannotDraw =
|
|
401
|
+
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 service in ${editSettings.images}.`;
|
|
402
|
+
const cannotDraw = `Update the Codex CLI and check your ChatGPT plan includes image generation, or choose another image service in ${editSettings.images}.`;
|
|
402
403
|
function imageEnvironment(directory, source) {
|
|
403
404
|
const env = { PWD: directory };
|
|
404
405
|
const allowed = [
|
|
@@ -2,6 +2,7 @@ import { z } from "zod";
|
|
|
2
2
|
import { internalError } from "../../kernel/errors.js";
|
|
3
3
|
import { redact } from "../../kernel/log.js";
|
|
4
4
|
import { providerError } from "../../kernel/ports/model.js";
|
|
5
|
+
import { createRows, editSettings } from "../../kernel/ports/settings-places.js";
|
|
5
6
|
import { httpFailure, noImage, providerSaid, unreadable } from "../explain.js";
|
|
6
7
|
import { failureText, httpProviderError, kindOfStatus, requireKey, safeJson, } from "../http-failure.js";
|
|
7
8
|
import { modelAccessOf } from "../model-access.js";
|
|
@@ -114,7 +115,7 @@ export function falImage(deps) {
|
|
|
114
115
|
if (req.reference !== undefined && edit === undefined)
|
|
115
116
|
throw providerError({
|
|
116
117
|
kind: "unsupported",
|
|
117
|
-
message:
|
|
118
|
+
message: `This fal.ai model can't use an establishing image as a reference. Choose FLUX.2 or Nano Banana 2 as the image model in ${createRows.images} or ${editSettings.images}, or set Establishing image to Off there.`,
|
|
118
119
|
});
|
|
119
120
|
const response = await deps.fetch(`${falBase}/${edit ?? req.model}`, {
|
|
120
121
|
method: "POST",
|
|
@@ -177,7 +178,7 @@ export function falImage(deps) {
|
|
|
177
178
|
if (current.error)
|
|
178
179
|
throw providerError({
|
|
179
180
|
kind: "other",
|
|
180
|
-
message: providerSaid("fal.ai", "could not make the video clip", redact(current.error),
|
|
181
|
+
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 ${editSettings.look}.`),
|
|
181
182
|
});
|
|
182
183
|
if (current.status === "COMPLETED")
|
|
183
184
|
break;
|
|
@@ -213,7 +214,7 @@ function refused(answer, prompt) {
|
|
|
213
214
|
if (flags.length > 0 && flags.every((flagged) => flagged)) {
|
|
214
215
|
throw providerError({
|
|
215
216
|
kind: "refusal",
|
|
216
|
-
message: `fal.ai's safety checker blocked the image for this prompt: "${redact(prompt)}". Reword the image prompt in
|
|
217
|
+
message: `fal.ai's safety checker blocked the image for this prompt: "${redact(prompt)}". Reword the image prompt in ${editSettings.images}, or choose another image service there.`,
|
|
217
218
|
});
|
|
218
219
|
}
|
|
219
220
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { redact } from "../../kernel/log.js";
|
|
3
3
|
import { providerError } from "../../kernel/ports/model.js";
|
|
4
|
+
import { editSettings } from "../../kernel/ports/settings-places.js";
|
|
4
5
|
import { httpFailure, noImage, quoted, refusedImage, unreadable } from "../explain.js";
|
|
5
6
|
import { failureText, httpProviderError, kindOfStatus, requireKey, safeJson, } from "../http-failure.js";
|
|
6
7
|
import { modelAccessOf } from "../model-access.js";
|
|
@@ -118,7 +119,7 @@ function decode(part) {
|
|
|
118
119
|
const claimed = part.mime_type ?? "nothing";
|
|
119
120
|
throw providerError({
|
|
120
121
|
kind: "other",
|
|
121
|
-
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
|
|
122
|
+
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 ${editSettings.images}.`,
|
|
122
123
|
});
|
|
123
124
|
}
|
|
124
125
|
return { bytes, mime };
|
|
@@ -158,7 +159,7 @@ async function failure(response) {
|
|
|
158
159
|
const detail = message || response.statusText;
|
|
159
160
|
const refused = kind === "refusal" && refusalWords.test(message);
|
|
160
161
|
return httpProviderError(response, kind, kind === "unsupported"
|
|
161
|
-
? `Google gives this API key no image quota for this model${quoted(`error ${String(response.status)}`, detail)}. Retrying will not help: turn on billing for the key's project in Google AI Studio, or choose another image
|
|
162
|
+
? `Google gives this API key no image quota for this model${quoted(`error ${String(response.status)}`, detail)}. Retrying will not help: turn on billing for the key's project in Google AI Studio, or choose another image service in ${editSettings.images}.`
|
|
162
163
|
: // PERMISSION_DENIED and FAILED_PRECONDITION also cover keys without access, billing
|
|
163
164
|
// and region limits, so they read as a content refusal only when Google says so.
|
|
164
165
|
refused
|
|
@@ -66,13 +66,13 @@ function requireKey(deps) {
|
|
|
66
66
|
if (key === undefined || key === "")
|
|
67
67
|
throw providerError({
|
|
68
68
|
kind: "missing_key",
|
|
69
|
-
message: "Save this provider's API key in Settings →
|
|
69
|
+
message: "Save this provider's API key in Settings → Keys & services to load its models.",
|
|
70
70
|
});
|
|
71
71
|
return key;
|
|
72
72
|
}
|
|
73
73
|
function unavailable() {
|
|
74
74
|
return providerError({
|
|
75
75
|
kind: "other",
|
|
76
|
-
message: "Slopify could not load this provider's model list. Check the API key in Settings →
|
|
76
|
+
message: "Slopify could not load this provider's model list. Check the API key in Settings → Keys & services and your internet connection, then refresh the list.",
|
|
77
77
|
});
|
|
78
78
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { redact } from "../../kernel/log.js";
|
|
3
3
|
import { providerError } from "../../kernel/ports/model.js";
|
|
4
|
+
import { editSettings } from "../../kernel/ports/settings-places.js";
|
|
4
5
|
import { httpFailure, noImage, refusedImage, unreadable } from "../explain.js";
|
|
5
6
|
import { failureText, httpProviderError, kindOfStatus, requireKey, safeJson, } from "../http-failure.js";
|
|
6
7
|
import { modelAccessOf } from "../model-access.js";
|
|
@@ -117,7 +118,7 @@ function decode(b64) {
|
|
|
117
118
|
if (mime === undefined) {
|
|
118
119
|
throw providerError({
|
|
119
120
|
kind: "other",
|
|
120
|
-
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
|
|
121
|
+
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 ${editSettings.images}.`,
|
|
121
122
|
});
|
|
122
123
|
}
|
|
123
124
|
return { bytes, mime };
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { redact } from "../../kernel/log.js";
|
|
3
3
|
import { providerError } from "../../kernel/ports/model.js";
|
|
4
|
+
import { createRows, editSettings } from "../../kernel/ports/settings-places.js";
|
|
4
5
|
import { httpFailure, noImage, providerSaid, refusedImage, unreadable } from "../explain.js";
|
|
5
6
|
import { failureText, httpProviderError, kindOfStatus, requireKey, safeJson, } from "../http-failure.js";
|
|
6
7
|
import { modelAccessOf } from "../model-access.js";
|
|
@@ -51,7 +52,7 @@ export function replicateImage(deps) {
|
|
|
51
52
|
if (req.reference !== undefined)
|
|
52
53
|
throw providerError({
|
|
53
54
|
kind: "unsupported",
|
|
54
|
-
message:
|
|
55
|
+
message: `Replicate's image models can't use an establishing image as a reference. Choose Codex CLI, OpenAI, Google or fal.ai as the image service in ${createRows.images} or ${editSettings.images}, or set Establishing image to Off there.`,
|
|
55
56
|
});
|
|
56
57
|
const response = await deps.fetch(`${replicateBase}/models/${req.model}/predictions`, {
|
|
57
58
|
method: "POST",
|
|
@@ -172,7 +173,7 @@ function declined(current) {
|
|
|
172
173
|
kind: refusal ? "refusal" : "other",
|
|
173
174
|
message: refusal
|
|
174
175
|
? refusedImage("Replicate", message)
|
|
175
|
-
: providerSaid("Replicate", "could not make the image", message,
|
|
176
|
+
: providerSaid("Replicate", "could not make the image", message, `Use Try again; if it keeps happening, reword the image prompt in ${editSettings.images} or choose another image model there.`),
|
|
176
177
|
});
|
|
177
178
|
}
|
|
178
179
|
function auth(deps) {
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { providerError } from "../../kernel/ports/model.js";
|
|
2
|
+
import { editSettings } from "../../kernel/ports/settings-places.js";
|
|
2
3
|
import { describeBytes } from "./bytes.js";
|
|
3
4
|
// What the two image-to-video adapters share: the still goes up inline as a data URI (the
|
|
4
5
|
// render sends a small JPEG, `slices/rebuild/runtime-animate.ts`), and the clip comes back
|
|
@@ -22,7 +23,7 @@ export async function downloadVideo(download) {
|
|
|
22
23
|
if (!sniffVideo(bytes))
|
|
23
24
|
throw providerError({
|
|
24
25
|
kind: "other",
|
|
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
|
|
26
|
+
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 ${editSettings.look}.`,
|
|
26
27
|
});
|
|
27
28
|
return { bytes, mime: "video/mp4" };
|
|
28
29
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { safeJson } from "./http-failure.js";
|
|
2
2
|
import { cartesiaVersion } from "./tts/cartesia.js";
|
|
3
|
-
// Settings →
|
|
3
|
+
// Settings → Keys & services' Test button, per provider. Some answer a key they do not recognise
|
|
4
4
|
// with something other than 401: Google with 400 API_KEY_INVALID, ElevenLabs with 400
|
|
5
5
|
// invalid_api_key, Inworld with 403 "does not exist". Observed on 2026-09-27; their
|
|
6
6
|
// documentation says 401.
|
|
@@ -32,6 +32,6 @@ export async function nodeAntigravityModels(binary, read = nodeReadCliOutput) {
|
|
|
32
32
|
}
|
|
33
33
|
catch {
|
|
34
34
|
// Do not pass the CLI's raw output or home paths to the browser.
|
|
35
|
-
throw new Error('Slopify could not get the model list from the Antigravity CLI (it ran "agy models"). Check agy is installed and signed in (Settings →
|
|
35
|
+
throw new Error('Slopify could not get the model list from the Antigravity CLI (it ran "agy models"). Check agy is installed and signed in (Settings → Keys & services), then refresh the list, or type a model slug that "agy models" shows.');
|
|
36
36
|
}
|
|
37
37
|
}
|
|
@@ -5,6 +5,7 @@ import { z } from "zod";
|
|
|
5
5
|
import { redact } from "../../kernel/log.js";
|
|
6
6
|
import { documentMessages } from "../../kernel/ports/llm-documents.js";
|
|
7
7
|
import { providerError } from "../../kernel/ports/model.js";
|
|
8
|
+
import { editSettings } from "../../kernel/ports/settings-places.js";
|
|
8
9
|
import { cliCheck, cliName, cliReported, commandOf } from "../explain.js";
|
|
9
10
|
import { accessField } from "../model-access.js";
|
|
10
11
|
import { nodeAntigravityModels } from "./antigravity-models.js";
|
|
@@ -77,7 +78,7 @@ export function antigravityLlm(deps) {
|
|
|
77
78
|
if (/^gemini$/i.test(commandOf(binary)))
|
|
78
79
|
throw providerError({
|
|
79
80
|
kind: "unsupported",
|
|
80
|
-
message: `The saved program path for the Antigravity CLI (${binary}) still points at the Gemini CLI, which Google has retired. In Settings →
|
|
81
|
+
message: `The saved program path for the Antigravity CLI (${binary}) still points at the Gemini CLI, which Google has retired. In Settings → Keys & services, clear the Antigravity CLI path or set it to the agy program, then use Try again.`,
|
|
81
82
|
});
|
|
82
83
|
// The documents travel inline, as with OpenRouter: headless agy refuses MCP tools it was
|
|
83
84
|
// not pre-approved for, so a research reader tool would be silently unavailable.
|
|
@@ -160,7 +161,7 @@ export function antigravityLlm(deps) {
|
|
|
160
161
|
}
|
|
161
162
|
req.signal.throwIfAborted();
|
|
162
163
|
if (run === undefined)
|
|
163
|
-
throw failure(binary, req.model, "", `The ${cliName(binary)} could not be started. Check it is installed and set up in Settings →
|
|
164
|
+
throw failure(binary, req.model, "", `The ${cliName(binary)} could not be started. Check it is installed and set up in Settings → Keys & services, then use Try again.`);
|
|
164
165
|
if (authRequired(run.stderr()))
|
|
165
166
|
throw authFailure();
|
|
166
167
|
throw failure(binary, req.model, run.stderr(), ended === undefined ? stuckCli(binary) : endedWithout(binary, ended, run.stderr()));
|
|
@@ -191,11 +192,11 @@ function emptyAnswer(binary, stderr) {
|
|
|
191
192
|
if (/no output produced|auto-denied|permission/i.test(stderr))
|
|
192
193
|
return providerError({
|
|
193
194
|
kind: "other",
|
|
194
|
-
message: `The ${cliName(binary)} tried to use a tool Slopify does not allow (it said: ${redact(stderr.trim()).slice(0, 300)}), so it gave no answer. Use Try again; if it keeps happening, choose another
|
|
195
|
+
message: `The ${cliName(binary)} tried to use a tool Slopify does not allow (it said: ${redact(stderr.trim()).slice(0, 300)}), so it gave no answer. Use Try again; if it keeps happening, choose another service or model in ${editSettings.writer}.`,
|
|
195
196
|
});
|
|
196
197
|
return providerError({
|
|
197
198
|
kind: "other",
|
|
198
|
-
message: `The ${cliName(binary)} answered with no text. Use Try again; if it keeps happening, run agy in a terminal to check it works, or choose another model in
|
|
199
|
+
message: `The ${cliName(binary)} answered with no text. Use Try again; if it keeps happening, run agy in a terminal to check it works, or choose another model in ${editSettings.writer}.`,
|
|
199
200
|
});
|
|
200
201
|
}
|
|
201
202
|
// `message` is what the CLI said; `sentence` replaces the default wording when Slopify
|
|
@@ -208,7 +209,7 @@ function failure(binary, model, message, sentence, now = Date.now) {
|
|
|
208
209
|
kind: "unsupported",
|
|
209
210
|
// agy lists models a plan can't run and refuses them with this sentence.
|
|
210
211
|
access: "no-access",
|
|
211
|
-
message: `The ${cliName(binary)} does not offer the model "${model}". Run "agy models" in a terminal to see the ones it has, pick one in
|
|
212
|
+
message: `The ${cliName(binary)} does not offer the model "${model}". Run "agy models" in a terminal to see the ones it has, pick one in ${editSettings.writer}, then use Continue the run.`,
|
|
212
213
|
});
|
|
213
214
|
if (/quota|resource.?exhausted|capacity/i.test(message) &&
|
|
214
215
|
/exhaust|exceed|used up|reached|limit/i.test(message)) {
|
|
@@ -222,7 +223,7 @@ function failure(binary, model, message, sentence, now = Date.now) {
|
|
|
222
223
|
if (waitMs !== undefined && waitMs <= 5 * 60_000)
|
|
223
224
|
return providerError({
|
|
224
225
|
kind: "rate_limit",
|
|
225
|
-
message: `The ${cliName(binary)} is busy: Google's capacity for this model is used for the moment (it said: ${redact(message)}). Slopify tries again by itself shortly; if it keeps happening, run fewer projects at once or choose another model in
|
|
226
|
+
message: `The ${cliName(binary)} is busy: Google's capacity for this model is used for the moment (it said: ${redact(message)}). Slopify tries again by itself shortly; if it keeps happening, run fewer projects at once or choose another model in ${editSettings.writer}.`,
|
|
226
227
|
retryAfterMs: Math.max(1000, waitMs),
|
|
227
228
|
});
|
|
228
229
|
return providerError({
|
|
@@ -225,7 +225,7 @@ function failed(cli, said) {
|
|
|
225
225
|
return { type: "failed", message: login.message };
|
|
226
226
|
return {
|
|
227
227
|
type: "failed",
|
|
228
|
-
message: `The ${cliName(cli === "claude-code" ? "claude" : "codex")} stopped with an error: ${redact(said).slice(0, 500)}. Send your message again; if it keeps happening, check the CLI in Settings →
|
|
228
|
+
message: `The ${cliName(cli === "claude-code" ? "claude" : "codex")} stopped with an error: ${redact(said).slice(0, 500)}. Send your message again; if it keeps happening, check the CLI in Settings → Keys & services.`,
|
|
229
229
|
};
|
|
230
230
|
}
|
|
231
231
|
// One JSONL line of the CLI's stream as what the chat shows. A stateful reader: Codex reports
|
|
@@ -36,7 +36,7 @@ const responseSchema = z.object({
|
|
|
36
36
|
});
|
|
37
37
|
const supportedEfforts = ["low", "medium", "high", "xhigh"];
|
|
38
38
|
function unavailable() {
|
|
39
|
-
return new Error("Slopify could not get the model list from the Claude Code CLI. Check the CLI is installed and signed in (Settings →
|
|
39
|
+
return new Error("Slopify could not get the model list from the Claude Code CLI. Check the CLI is installed and signed in (Settings → Keys & services), then refresh the list, or type an exact model ID.");
|
|
40
40
|
}
|
|
41
41
|
function discoveryEnv() {
|
|
42
42
|
const names = [
|
|
@@ -2,6 +2,7 @@ import { z } from "zod";
|
|
|
2
2
|
import { redact } from "../../kernel/log.js";
|
|
3
3
|
import { providerError } from "../../kernel/ports/model.js";
|
|
4
4
|
import { isoOfEpoch } from "../../kernel/ports/plan-limits.js";
|
|
5
|
+
import { editSettings } from "../../kernel/ports/settings-places.js";
|
|
5
6
|
import { cliCheck, cliReported, commandOf } from "../explain.js";
|
|
6
7
|
import { nodeClaudeCodeModels } from "./claude-code-models.js";
|
|
7
8
|
import { cliLoginError } from "./cli-login-error.js";
|
|
@@ -284,7 +285,7 @@ export function claudeCodeLlm(deps) {
|
|
|
284
285
|
req.signal.throwIfAborted();
|
|
285
286
|
// The stream ended with no result event at all.
|
|
286
287
|
if (!run)
|
|
287
|
-
throw new Error("The Claude Code CLI could not be started. Check it is installed and set up in Settings →
|
|
288
|
+
throw new Error("The Claude Code CLI could not be started. Check it is installed and set up in Settings → Keys & services, then use Try again.");
|
|
288
289
|
const login = cliLoginError("claude-code", run.stderr());
|
|
289
290
|
if (login)
|
|
290
291
|
throw login;
|
|
@@ -322,7 +323,7 @@ function nextStep(binary, status) {
|
|
|
322
323
|
if (status === 429)
|
|
323
324
|
return "Your Claude usage limit may be used up: wait until it resets, then use Try again.";
|
|
324
325
|
if (status === 404)
|
|
325
|
-
return
|
|
326
|
+
return `The chosen model may not exist or not be available to your account: choose another in ${editSettings.writer}, then use Try again.`;
|
|
326
327
|
return cliCheck(binary);
|
|
327
328
|
}
|
|
328
329
|
// `modelUsage` is keyed by model id and a run may touch more than one - a fallback model,
|
|
@@ -59,7 +59,7 @@ export async function nodeCodexModels(env = process.env, binary = "codex", timeo
|
|
|
59
59
|
}
|
|
60
60
|
catch {
|
|
61
61
|
// Do not expose cache contents, home paths or raw JSON errors to the browser.
|
|
62
|
-
throw new Error("Slopify could not get the model list from the Codex CLI. Check the CLI is installed and signed in (Settings →
|
|
62
|
+
throw new Error("Slopify could not get the model list from the Codex CLI. Check the CLI is installed and signed in (Settings → Keys & services), then refresh the list, or type an exact model ID.");
|
|
63
63
|
}
|
|
64
64
|
}
|
|
65
65
|
}
|
|
@@ -6,6 +6,7 @@ import { fileURLToPath } from "node:url";
|
|
|
6
6
|
import { z } from "zod";
|
|
7
7
|
import { documentIndex, llmDocumentsSchema, } from "../../kernel/ports/llm-documents.js";
|
|
8
8
|
import { providerError } from "../../kernel/ports/model.js";
|
|
9
|
+
import { editSettings } from "../../kernel/ports/settings-places.js";
|
|
9
10
|
export const documentServerName = "slopify_research";
|
|
10
11
|
export const documentTool = "read_document";
|
|
11
12
|
export const documentToolName = `mcp__${documentServerName}__${documentTool}`;
|
|
@@ -16,7 +17,7 @@ export function documentWorkspace(input) {
|
|
|
16
17
|
if (!parsed.success)
|
|
17
18
|
throw providerError({
|
|
18
19
|
kind: "unsupported",
|
|
19
|
-
message:
|
|
20
|
+
message: `The research notes are too large, or not in a form the AI model can be given, so nothing was sent. Make the research notes shorter in ${editSettings.article}, then use Try again; if it keeps happening, use Download diagnostics in Settings and report it.`,
|
|
20
21
|
});
|
|
21
22
|
const documents = parsed.data;
|
|
22
23
|
const directory = mkdtempSync(join(tmpdir(), "slopify-documents-"));
|
|
@@ -82,6 +83,6 @@ export function documentWorkspace(input) {
|
|
|
82
83
|
function unread() {
|
|
83
84
|
return providerError({
|
|
84
85
|
kind: "unavailable",
|
|
85
|
-
message:
|
|
86
|
+
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 ${editSettings.writer}.`,
|
|
86
87
|
});
|
|
87
88
|
}
|
|
@@ -2,6 +2,7 @@ import { copyFileSync, mkdtempSync, rmSync } from "node:fs";
|
|
|
2
2
|
import { tmpdir } from "node:os";
|
|
3
3
|
import { basename, extname, join } from "node:path";
|
|
4
4
|
import { providerError } from "../../kernel/ports/model.js";
|
|
5
|
+
import { editSettings } from "../../kernel/ports/settings-places.js";
|
|
5
6
|
const extensions = new Set([".png", ".jpg", ".jpeg", ".webp"]);
|
|
6
7
|
// ceiling: a review shows one image, or a short's handful plus the establishing image.
|
|
7
8
|
export const imagesMax = 12;
|
|
@@ -47,12 +48,12 @@ export function readFileName(path) {
|
|
|
47
48
|
export function unseenImages() {
|
|
48
49
|
return providerError({
|
|
49
50
|
kind: "unavailable",
|
|
50
|
-
message:
|
|
51
|
+
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 ${editSettings.checks}.`,
|
|
51
52
|
});
|
|
52
53
|
}
|
|
53
54
|
export function noImages(provider) {
|
|
54
55
|
return providerError({
|
|
55
56
|
kind: "unsupported",
|
|
56
|
-
message: `Slopify can't show pictures to ${provider}, so a review that needs them was not sent. In
|
|
57
|
+
message: `Slopify can't show pictures to ${provider}, so a review that needs them was not sent. In ${editSettings.checks}, choose Claude Code or Codex as the reviewer, or set the picture reviews to Off, then use Try again.`,
|
|
57
58
|
});
|
|
58
59
|
}
|
|
@@ -2,6 +2,7 @@ import { z } from "zod";
|
|
|
2
2
|
import { redact } from "../../kernel/log.js";
|
|
3
3
|
import { documentMessages } from "../../kernel/ports/llm-documents.js";
|
|
4
4
|
import { providerError } from "../../kernel/ports/model.js";
|
|
5
|
+
import { editSettings } from "../../kernel/ports/settings-places.js";
|
|
5
6
|
import { droppedStream, httpFailure, providerSaid, refreshList, unreadable } from "../explain.js";
|
|
6
7
|
import { failureText, httpProviderError, kindOfStatus, requireKey, safeJson, } from "../http-failure.js";
|
|
7
8
|
import { accessField, modelAccessOf } from "../model-access.js";
|
|
@@ -164,7 +165,7 @@ async function failure(response, next) {
|
|
|
164
165
|
function streamFailure(status, detail) {
|
|
165
166
|
return status >= 400
|
|
166
167
|
? httpFailure({ provider: "OpenRouter", status, detail })
|
|
167
|
-
: providerSaid("OpenRouter", "stopped part-way through the answer", detail,
|
|
168
|
+
: providerSaid("OpenRouter", "stopped part-way through the answer", detail, `Use Try again; if it keeps happening, choose another model in ${editSettings.writer}.`);
|
|
168
169
|
}
|
|
169
170
|
function statusOf(code) {
|
|
170
171
|
return typeof code === "number" ? code : Number(code ?? 0);
|
|
@@ -3,6 +3,7 @@ import { z } from "zod";
|
|
|
3
3
|
import { cliCommand } from "../../kernel/cli-command.js";
|
|
4
4
|
import { redact } from "../../kernel/log.js";
|
|
5
5
|
import { providerError } from "../../kernel/ports/model.js";
|
|
6
|
+
import { editSettings } from "../../kernel/ports/settings-places.js";
|
|
6
7
|
import { cliName, commandOf, quoted } from "../explain.js";
|
|
7
8
|
import { safeJson } from "../http-failure.js";
|
|
8
9
|
// ceiling: the last 8 KiB of stderr is kept. A CLI that writes megabytes of progress there
|
|
@@ -140,7 +141,7 @@ export function cliInput(text) {
|
|
|
140
141
|
if (Buffer.byteLength(text) >= 8 * 1024 * 1024)
|
|
141
142
|
throw providerError({
|
|
142
143
|
kind: "unsupported",
|
|
143
|
-
message:
|
|
144
|
+
message: `This prompt is larger than the CLI accepts (8 MB), so nothing was sent. Make the article or its research notes shorter in ${editSettings.article}, or choose an API service such as OpenRouter under Written by there, then use Try again.`,
|
|
144
145
|
});
|
|
145
146
|
return text;
|
|
146
147
|
}
|
|
@@ -169,7 +170,7 @@ export function promptOf(messages) {
|
|
|
169
170
|
export function endedWithout(binary, ended, stderr) {
|
|
170
171
|
const said = redact(stderr.trim());
|
|
171
172
|
if (ended.error !== null)
|
|
172
|
-
return `The ${cliName(binary)} could not be started${quoted("", redact(ended.error.message))}. Check it is installed and set up in Settings →
|
|
173
|
+
return `The ${cliName(binary)} could not be started${quoted("", redact(ended.error.message))}. Check it is installed and set up in Settings → Keys & services, then use Try again.`;
|
|
173
174
|
const how = ended.code === null ? "killed by the system" : `exit code ${String(ended.code)}`;
|
|
174
175
|
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.`;
|
|
175
176
|
}
|