@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,5 +1,5 @@
|
|
|
1
1
|
import { admittedUnfinished } from "../admission/admitted-work.js";
|
|
2
|
-
import { oneProject } from "../admission/repo.js";
|
|
2
|
+
import { liveProject, oneProject } from "../admission/repo.js";
|
|
3
3
|
export function markUploaded(db, projectId, uploaded, at) {
|
|
4
4
|
if (db.prepare("SELECT 1 FROM projects WHERE id=?").get(projectId) === undefined)
|
|
5
5
|
return { ok: false, reason: "not-found" };
|
|
@@ -35,6 +35,42 @@ export function uploadedAndSettled(db, projectId) {
|
|
|
35
35
|
.all(...one.params)
|
|
36
36
|
.map((row) => String(row.project_id)));
|
|
37
37
|
}
|
|
38
|
+
// A finished project an edit left with outdated files reads done too, not "Stopped": every step
|
|
39
|
+
// still to run made its files before the edit (a selected output of the current version, now
|
|
40
|
+
// outdated), and nothing runs it until the person chooses to make those files again, which shows
|
|
41
|
+
// the cost first. Renaming a project with a PDF is one: its PDF prints the title. A step that
|
|
42
|
+
// never made a file, a failed or canceled step, a step a run admitted (`admittedUnfinished`, as
|
|
43
|
+
// a restart holds it), a pause or a queued run keep it unsettled, so a run a restart stopped
|
|
44
|
+
// still reads Stopped. So does a version saved to make a section again (Make … again, a review's
|
|
45
|
+
// redo) whose run was refused, say for a missing key: the person asked for that work, and Continue
|
|
46
|
+
// the run starts it. One project's alone when `projectId` is given.
|
|
47
|
+
export function editedAndSettled(db, projectId) {
|
|
48
|
+
const one = oneProject("h", projectId);
|
|
49
|
+
const unfinished = "s.project_id=h.project_id AND s.state NOT IN ('done','provided','skipped')";
|
|
50
|
+
return new Set(db
|
|
51
|
+
.prepare(`SELECT h.project_id FROM project_heads h
|
|
52
|
+
WHERE ${liveProject("h", "project_id")}
|
|
53
|
+
AND EXISTS(SELECT 1 FROM stages s WHERE ${unfinished})
|
|
54
|
+
AND NOT EXISTS(SELECT 1 FROM stages s WHERE ${unfinished}
|
|
55
|
+
AND (s.state!='pending' OR NOT EXISTS(SELECT 1 FROM revision_outputs o
|
|
56
|
+
WHERE o.project_id=h.project_id AND o.revision_id=h.revision_id AND o.selected=1
|
|
57
|
+
AND o.state='outdated' AND json_extract(o.descriptor,'$.stageKind')=s.kind)))
|
|
58
|
+
AND NOT EXISTS(SELECT 1 FROM revision_work w WHERE w.project_id=h.project_id
|
|
59
|
+
AND w.state IN ('pending','running')
|
|
60
|
+
AND (w.dispatch_state!='held' OR ${admittedUnfinished("w")}))
|
|
61
|
+
AND NOT EXISTS(SELECT 1 FROM project_recovery_requests r WHERE r.project_id=h.project_id
|
|
62
|
+
AND r.intent_revision_id=h.revision_id)
|
|
63
|
+
AND NOT EXISTS(SELECT 1 FROM project_controls c WHERE c.project_id=h.project_id AND c.paused=1)
|
|
64
|
+
AND NOT EXISTS(SELECT 1 FROM project_queue q WHERE q.project_id=h.project_id AND q.state='queued')${one.sql}`)
|
|
65
|
+
.all(...one.params)
|
|
66
|
+
.map((row) => String(row.project_id)));
|
|
67
|
+
}
|
|
68
|
+
// Every project that reads done though a step is left: uploaded with nothing left to run
|
|
69
|
+
// (`uploadedAndSettled`), or finished and left only with files an edit made outdated
|
|
70
|
+
// (`editedAndSettled`).
|
|
71
|
+
export function settledProjects(db, projectId) {
|
|
72
|
+
return new Set([...uploadedAndSettled(db, projectId), ...editedAndSettled(db, projectId)]);
|
|
73
|
+
}
|
|
38
74
|
// "Keep as is": a waiting run the person leaves as it is, until the project's next edit. Set on
|
|
39
75
|
// the current head revision; the listing counts it only while that revision is still the head.
|
|
40
76
|
export function setAside(db, projectId, aside, at) {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
//
|
|
2
|
-
//
|
|
1
|
+
// A settled project (`settledProjects`: uploaded with nothing left to run, or finished and left
|
|
2
|
+
// only with files an edit made outdated) reads done where its run would read paused or pending:
|
|
3
|
+
// the Projects list, a project's page and the calendar.
|
|
3
4
|
export function settledStatus(status, settled) {
|
|
4
5
|
return settled && (status === "paused" || status === "pending") ? "done" : status;
|
|
5
6
|
}
|
|
@@ -116,9 +116,9 @@ const editListSchema = z
|
|
|
116
116
|
export function readEditList(value) {
|
|
117
117
|
const version = typeof value === "object" && value !== null && "version" in value ? value.version : undefined;
|
|
118
118
|
if (version !== editListVersion)
|
|
119
|
-
throw new Error(`This video's edit list is version ${String(version)}, and this Slopify reads version ${editListVersion}. Update Slopify, or
|
|
119
|
+
throw new Error(`This video's edit list is version ${String(version)}, and this Slopify reads version ${editListVersion}. Update Slopify, or press Make the video again in the project's Video section to plan it again.`);
|
|
120
120
|
const parsed = editListSchema.safeParse(value);
|
|
121
121
|
if (!parsed.success)
|
|
122
|
-
throw new Error(`This video's edit list is damaged (${parsed.error.issues[0]?.path.join(".") || "the list"}: ${parsed.error.issues[0]?.message ?? "not readable"}).
|
|
122
|
+
throw new Error(`This video's edit list is damaged (${parsed.error.issues[0]?.path.join(".") || "the list"}: ${parsed.error.issues[0]?.message ?? "not readable"}). Press Make the video again in the project's Video section to plan it again.`);
|
|
123
123
|
return parsed.data;
|
|
124
124
|
}
|
|
@@ -177,7 +177,7 @@ export function videoEditRows(edit) {
|
|
|
177
177
|
],
|
|
178
178
|
["Vignette", lookLevelLabels[edit.vignette]],
|
|
179
179
|
["Film grain", lookLevelLabels[edit.grain]],
|
|
180
|
-
["
|
|
180
|
+
["Color grade", colorGradeLabels[edit.grade]],
|
|
181
181
|
["Atmosphere", atmosphereLabels[edit.atmosphere]],
|
|
182
182
|
["Chapter cards", edit.chapterCards ? "On" : "Off"],
|
|
183
183
|
[
|
|
@@ -55,7 +55,15 @@ export function codecArgs(encode, role, look) {
|
|
|
55
55
|
: []),
|
|
56
56
|
];
|
|
57
57
|
}
|
|
58
|
-
|
|
58
|
+
// Said to a person who may have no NVIDIA card at all: stock ffmpeg builds list the NVIDIA
|
|
59
|
+
// encoder everywhere, so a failed test usually means there is no card, not a broken one. What
|
|
60
|
+
// the test printed goes under Details (`detail`), never into these sentences.
|
|
61
|
+
export const noNvenc = "No NVIDIA graphics card was found on this computer.";
|
|
62
|
+
export const failedNvenc = "No working NVIDIA graphics card was found on this computer.";
|
|
63
|
+
// The reason with the test's output, for the log and a run's notice.
|
|
64
|
+
export function nvencWhy(status) {
|
|
65
|
+
return status.detail === undefined ? status.reason : `${status.reason} (${status.detail})`;
|
|
66
|
+
}
|
|
59
67
|
// The renders' own settings on a tiny picture: a driver or card that refuses any of them fails
|
|
60
68
|
// here rather than in a render. NVENC refuses anything under about 145 pixels across.
|
|
61
69
|
const testArgs = [
|
|
@@ -84,7 +92,8 @@ export function createNvencProbe(deps) {
|
|
|
84
92
|
return () => {
|
|
85
93
|
answer ??= probe(deps).catch((error) => ({
|
|
86
94
|
available: false,
|
|
87
|
-
reason:
|
|
95
|
+
reason: noNvenc,
|
|
96
|
+
detail: errorText(error),
|
|
88
97
|
}));
|
|
89
98
|
return answer;
|
|
90
99
|
};
|
|
@@ -112,7 +121,7 @@ async function probe(deps) {
|
|
|
112
121
|
}
|
|
113
122
|
if (failed !== undefined) {
|
|
114
123
|
deps.log.write("info", "video.encoder", { detail: failed });
|
|
115
|
-
return { available: false, reason:
|
|
124
|
+
return { available: false, reason: failedNvenc, detail: failed };
|
|
116
125
|
}
|
|
117
126
|
return { available: false, reason: noNvenc };
|
|
118
127
|
}
|
|
@@ -133,11 +142,11 @@ export async function renderMachine(deps) {
|
|
|
133
142
|
};
|
|
134
143
|
deps.log.write("warn", "video.encoder", {
|
|
135
144
|
projectId: deps.projectId,
|
|
136
|
-
detail: `NVIDIA encoder chosen but unavailable; encoding on the processor. ${status
|
|
145
|
+
detail: `NVIDIA encoder chosen but unavailable; encoding on the processor. ${nvencWhy(status)}`,
|
|
137
146
|
});
|
|
138
147
|
return {
|
|
139
148
|
machine: { encode: cpu, priority: settings.renderPriority },
|
|
140
|
-
notice: nvencFallbackNotice(status
|
|
149
|
+
notice: nvencFallbackNotice(nvencWhy(status)),
|
|
141
150
|
};
|
|
142
151
|
}
|
|
143
152
|
export function nvencFallbackNotice(reason) {
|
|
@@ -46,7 +46,7 @@ export function slideshowClips(edit) {
|
|
|
46
46
|
named.set(key, name);
|
|
47
47
|
const shot = edit.shots[segment.kind === "shot" ? segment.shot : segment.to];
|
|
48
48
|
if (shot === undefined)
|
|
49
|
-
throw new Error(internalError("the video's edit list lost a shot", "
|
|
49
|
+
throw new Error(internalError("the video's edit list lost a shot", "Press Make the video again in the project's Video section"));
|
|
50
50
|
clips.push({ name, segment, shot, frames: segment.count });
|
|
51
51
|
}
|
|
52
52
|
return name;
|
|
@@ -93,7 +93,7 @@ encode = defaultEncode) {
|
|
|
93
93
|
const chains = [];
|
|
94
94
|
const picture = (shot, from, label) => {
|
|
95
95
|
if (shot === undefined)
|
|
96
|
-
throw new Error(internalError("the video's edit list lost a shot", "
|
|
96
|
+
throw new Error(internalError("the video's edit list lost a shot", "Press Make the video again in the project's Video section"));
|
|
97
97
|
const at = inputs.filter((one) => one === "-i").length;
|
|
98
98
|
const built = pictureChain(edit, shot, from, segment.count, at, label, gradeFilter(edit.look));
|
|
99
99
|
inputs.push(...built.inputs);
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { editSettings } from "../../kernel/ports/settings-places.js";
|
|
1
2
|
import { chapterCuts, narrationShotFrames } from "./cuts.js";
|
|
2
3
|
import { editListVersion, } from "./edit-list.js";
|
|
3
4
|
import { chapterCardSeconds, endScreenSeconds } from "./edit-settings.js";
|
|
@@ -41,7 +42,7 @@ const figureGapSeconds = 1;
|
|
|
41
42
|
export function planRender(input) {
|
|
42
43
|
if (input.images.length === 0) {
|
|
43
44
|
// Admission permits Video only with an image source, so an empty set is a bug upstream.
|
|
44
|
-
throw new Error(
|
|
45
|
+
throw new Error(`The video needs at least one image, but there are none. Check ${editSettings.images}, then Try again.`);
|
|
45
46
|
}
|
|
46
47
|
const frame = frames[input.format];
|
|
47
48
|
const bed = input.body === undefined ? undefined : input.bed;
|
|
@@ -62,7 +62,7 @@ async function renderWith(run, encode) {
|
|
|
62
62
|
const font = edit.cardFont;
|
|
63
63
|
if (cards.length > 0) {
|
|
64
64
|
if (font === undefined)
|
|
65
|
-
throw new Error(internalError("the chapter cards have no font", "
|
|
65
|
+
throw new Error(internalError("the chapter cards have no font", "Press Make the video again in the project's Video section"));
|
|
66
66
|
mkdirSync(join(workspace, "fonts"), { mode: 0o700 });
|
|
67
67
|
copyFileSync(font.path, join(workspace, "fonts", `card${extname(font.path)}`));
|
|
68
68
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { editSettings } from "../../kernel/ports/settings-places.js";
|
|
1
2
|
import { parseScript } from "./script.js";
|
|
2
3
|
// Audiobooks from an existing text: the text model hands each passage to a speaker - the
|
|
3
4
|
// narrator's prose to the narrator, each quoted line to the character who says it - and
|
|
@@ -35,12 +36,12 @@ export function parseAttribution(answer, source, speakers) {
|
|
|
35
36
|
if (expected.length > 0 && kept / expected.length < 0.9)
|
|
36
37
|
return {
|
|
37
38
|
ok: false,
|
|
38
|
-
reason: `The speaker split left out ${String(expected.length - kept)} of the text's ${String(expected.length)} words. The
|
|
39
|
+
reason: `The speaker split left out ${String(expected.length - kept)} of the text's ${String(expected.length)} words. The AI model must hand every word to a speaker; Try again, or choose another model in ${editSettings.writer}.`,
|
|
39
40
|
};
|
|
40
41
|
if (got.length - kept > Math.max(3, expected.length * 0.1))
|
|
41
42
|
return {
|
|
42
43
|
ok: false,
|
|
43
|
-
reason: `The speaker split added ${String(got.length - kept)} words that are not in the text. The
|
|
44
|
+
reason: `The speaker split added ${String(got.length - kept)} words that are not in the text. The AI model must not write anything of its own; Try again, or choose another model in ${editSettings.writer}.`,
|
|
44
45
|
};
|
|
45
46
|
return parsed;
|
|
46
47
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
+
import { createRows, editSettings } from "../../kernel/ports/settings-places.js";
|
|
2
3
|
// Multiple voices: a run whose narration is a script of speaker turns rather than one voice
|
|
3
4
|
// reading an article. Absent on a run is the Narration format, which is every project saved
|
|
4
5
|
// before this existed, and nothing about such a run reads anything in this slice.
|
|
@@ -136,23 +137,23 @@ export function voicesProblems(settings) {
|
|
|
136
137
|
if (speakers.length === 0)
|
|
137
138
|
problems.push({
|
|
138
139
|
field: "voices.speakers",
|
|
139
|
-
message:
|
|
140
|
+
message: `Add at least one speaker under Speakers (${createRows.narration}, or ${editSettings.narration}).`,
|
|
140
141
|
});
|
|
141
142
|
if (settings.source === "attribute" && settings.format !== "audiobook")
|
|
142
143
|
problems.push({
|
|
143
144
|
field: "voices.source",
|
|
144
|
-
message:
|
|
145
|
+
message: `Only audiobooks can hand an existing text's dialogue to speakers. Choose Write a script under Speakers (${createRows.narration}, or ${editSettings.narration}).`,
|
|
145
146
|
});
|
|
146
147
|
if ((settings.format === "podcast" || settings.format === "interview") && speakers.length < 2)
|
|
147
148
|
problems.push({
|
|
148
149
|
field: "voices.speakers",
|
|
149
|
-
message: `A ${voiceFormatLabels[settings.format].toLowerCase()} needs at least two speakers. Add one under Speakers (
|
|
150
|
+
message: `A ${voiceFormatLabels[settings.format].toLowerCase()} needs at least two speakers. Add one under Speakers (${createRows.narration}, or ${editSettings.narration}).`,
|
|
150
151
|
});
|
|
151
152
|
if ((settings.format === "audiobook" || settings.format === "drama") &&
|
|
152
153
|
!speakers.some((speaker) => speaker.role === "narrator"))
|
|
153
154
|
problems.push({
|
|
154
155
|
field: "voices.speakers",
|
|
155
|
-
message: `An ${settings.format === "drama" ? "radio drama" : "audiobook"} needs a narrator. Set one speaker's role to Narrator under Speakers (
|
|
156
|
+
message: `An ${settings.format === "drama" ? "radio drama" : "audiobook"} needs a narrator. Set one speaker's role to Narrator under Speakers (${createRows.narration}, or ${editSettings.narration}).`,
|
|
156
157
|
});
|
|
157
158
|
const names = new Set();
|
|
158
159
|
const ids = new Set();
|
|
@@ -181,7 +182,7 @@ export function voicesProblems(settings) {
|
|
|
181
182
|
if (speaker.voice.provider.trim() === "" || speaker.voice.model.trim() === "")
|
|
182
183
|
problems.push({
|
|
183
184
|
field: `${field}.voice`,
|
|
184
|
-
message: `Choose a voice
|
|
185
|
+
message: `Choose a voice service and model for ${name || "this speaker"}.`,
|
|
185
186
|
});
|
|
186
187
|
else if (speaker.voice.voice.trim() === "")
|
|
187
188
|
problems.push({
|
|
@@ -198,20 +199,20 @@ export function voicesProblems(settings) {
|
|
|
198
199
|
if (settings.book.title.trim() === "")
|
|
199
200
|
problems.push({
|
|
200
201
|
field: "voices.book.title",
|
|
201
|
-
message:
|
|
202
|
+
message: `Enter the book's title under Speakers (${createRows.narration}, or ${editSettings.narration}), or remove the book to make a project on its own.`,
|
|
202
203
|
});
|
|
203
204
|
if (!Number.isInteger(settings.book.chapter) ||
|
|
204
205
|
settings.book.chapter < 1 ||
|
|
205
206
|
settings.book.chapter > bookChapterMax)
|
|
206
207
|
problems.push({
|
|
207
208
|
field: "voices.book.chapter",
|
|
208
|
-
message: `Enter a chapter number from 1 to ${String(bookChapterMax)} under Speakers (
|
|
209
|
+
message: `Enter a chapter number from 1 to ${String(bookChapterMax)} under Speakers (${createRows.narration}, or ${editSettings.narration}).`,
|
|
209
210
|
});
|
|
210
211
|
}
|
|
211
212
|
if (!turnGapSteps.includes(settings.turnGapSeconds))
|
|
212
213
|
problems.push({
|
|
213
214
|
field: "voices.turnGapSeconds",
|
|
214
|
-
message:
|
|
215
|
+
message: `Pick a gap between turns from the list under Speakers (${createRows.narration}, or ${editSettings.narration}).`,
|
|
215
216
|
});
|
|
216
217
|
return problems;
|
|
217
218
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { createRows, editSettings } from "../../kernel/ports/settings-places.js";
|
|
1
2
|
import { voiceFormatLabels } from "./model.js";
|
|
2
3
|
const heading = /^#{1,6}\s+(.+?)\s*#*\s*$/;
|
|
3
4
|
const turnLine = /^\*{0,2}([^:*\n]{1,60}?)\*{0,2}\s*:\s*\*{0,2}\s*(.*)$/;
|
|
@@ -42,7 +43,7 @@ export function parseScript(markdown, speakers, keepMarkdown = false) {
|
|
|
42
43
|
ok: false,
|
|
43
44
|
reason: turn === null
|
|
44
45
|
? `Line ${String(number)} of the script is not a turn. Start every turn with a speaker's name and a colon (${names}), one turn per paragraph.`
|
|
45
|
-
: `Line ${String(number)} of the script is spoken by "${(turn[1] ?? "").trim()}", who is not one of the speakers (${names}). Use only these names, or add the speaker under Speakers (
|
|
46
|
+
: `Line ${String(number)} of the script is spoken by "${(turn[1] ?? "").trim()}", who is not one of the speakers (${names}). Use only these names, or add the speaker under Speakers (${createRows.narration}, or ${editSettings.narration}).`,
|
|
46
47
|
};
|
|
47
48
|
}
|
|
48
49
|
const spoken = [];
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { jsonBetween } from "../../kernel/json-answer.js";
|
|
3
|
+
import { editSettings } from "../../kernel/ports/settings-places.js";
|
|
3
4
|
import { alternativeTitles, descriptionMaxCharacters, hashtagsMax, minChapterSeconds, minChapters, pinnedCommentMaxCharacters, tagMaxCharacters, tagsMaxCharacters, titleMaxCharacters, } from "./model.js";
|
|
4
5
|
import { parseTimestamp, youtubeTimestamp } from "./timestamps.js";
|
|
5
6
|
import { keepsShape, titleShapeRule } from "./titles.js";
|
|
@@ -53,7 +54,7 @@ const answerSchema = z.object({
|
|
|
53
54
|
pinnedComment: z.string(),
|
|
54
55
|
titles: z.array(z.string()),
|
|
55
56
|
});
|
|
56
|
-
const fix =
|
|
57
|
+
const fix = `Try again, or choose another model in ${editSettings.writer}.`;
|
|
57
58
|
// Reads and checks the model's answer. The reason is the sentence the stage shows; the
|
|
58
59
|
// provider wrapper's `check` asks the model again while attempts remain.
|
|
59
60
|
export function checkDescriptionAnswer(text, durationSeconds,
|
|
@@ -59,7 +59,7 @@ export function readChannelLinksFor(db, channelId) {
|
|
|
59
59
|
// the generated text, the chapters fitted to YouTube's rules (`chapters.ts`; the last one's
|
|
60
60
|
// length only checked when the video's length is given), placeholders filled from the
|
|
61
61
|
// project's and its channel's links (one with no link stays as typed). For anything that hands
|
|
62
|
-
// the description on, such as
|
|
62
|
+
// the description on, such as Copy for YouTube Studio and the downloads. `chapterNotice` says what the
|
|
63
63
|
// fitting changed.
|
|
64
64
|
export function effectiveDescription(db, projectId, generated) {
|
|
65
65
|
const edits = readDescriptionEdits(db, projectId);
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
Slopify can write the text with the Claude Code, Codex or Antigravity command-line tools, and draw the images with Codex, using the sign-in you already have. You need no API key for those steps, and their calls count toward your plan's limits instead of costing per call.
|
|
4
4
|
|
|
5
|
-
**Where to find it:** Settings →
|
|
5
|
+
**Where to find it:** Settings → Keys & services → the **Writing services** list (Claude Code CLI, Codex CLI, Antigravity CLI) and the **Image services** list (Codex CLI).
|
|
6
6
|
|
|
7
7
|
## What each tool does
|
|
8
8
|
|
|
@@ -15,7 +15,7 @@ Slopify can write the text with the Claude Code, Codex or Antigravity command-li
|
|
|
15
15
|
|
|
16
16
|
Codex text and Codex images share one sign-in, one executable path and one plan allowance.
|
|
17
17
|
|
|
18
|
-
Codex CLI must be version 0.149.1 or newer. An older Codex shows **Needs
|
|
18
|
+
Codex CLI must be version 0.149.1 or newer. An older Codex shows **Needs fixing** and says to update it.
|
|
19
19
|
|
|
20
20
|
## Set up a CLI
|
|
21
21
|
|
|
@@ -24,11 +24,11 @@ Codex CLI must be version 0.149.1 or newer. An older Codex shows **Needs attenti
|
|
|
24
24
|
- Claude Code: `claude auth login`
|
|
25
25
|
- Codex: `codex login`
|
|
26
26
|
- Antigravity: run `agy` and sign in with your Google account (a browser opens; over SSH it shows a URL and a code).
|
|
27
|
-
3. Open **Settings →
|
|
28
|
-
4. Pick the tool in the **
|
|
29
|
-
5.
|
|
27
|
+
3. Open **Settings → Keys & services** (reload the page if it was already open).
|
|
28
|
+
4. Pick the tool in the **Writing services** or **Image services** list. Its row should say **Ready**, and its setup shows **Installed, version …** and the **Command** Slopify runs.
|
|
29
|
+
5. Press **Check every service** to confirm it is signed in.
|
|
30
30
|
|
|
31
|
-
On the very first launch, before any key is saved, Slopify looks for all three tools. If it finds any, it says so and picks one for text on new
|
|
31
|
+
On the very first launch, before any key is saved, Slopify looks for all three tools. If it finds any, it says so and picks one for text on new Create drafts, preferring Claude Code, then Codex, then Antigravity, and Codex for images when found. The CLIs can't speak, but the narration needs no key either: without a voice key, the [System voice](Providers-and-Keys#the-system-voice) reads it with your computer's own speech. See [First-Launch-and-Welcome](First-Launch-and-Welcome).
|
|
32
32
|
|
|
33
33
|
## Antigravity CLI: what is different
|
|
34
34
|
|
|
@@ -41,21 +41,21 @@ It works differently from the Gemini CLI in a few ways:
|
|
|
41
41
|
- **No timeout of its own.** `agy` waits as long as the model needs; Slopify's own stall checks end a run that stops making progress.
|
|
42
42
|
- **Busy messages are retried.** When several calls run at once, Google can answer that its capacity for the model is used for the moment. A reset of 5 minutes or less is retried by itself within seconds; a longer reset is handled as a plan limit and the run waits for it.
|
|
43
43
|
- **Its sign-in is checked with `agy models`.** Slopify runs the command: if it lists models, the CLI is **Signed in**; if it says "Please sign in", the CLI is signed out and the check gives the command to run (`agy`); anything else shows as unknown. `GEMINI_API_KEY` is not read.
|
|
44
|
-
- **Old model ids are gone.** Models come from `agy models`. A project that still names a Gemini CLI model such as `gemini-2.5-pro` stops with "The chosen model is not available…" until you pick a new one in **Edit
|
|
44
|
+
- **Old model ids are gone.** Models come from `agy models`. A project that still names a Gemini CLI model such as `gemini-2.5-pro` stops with "The chosen model is not available…" until you pick a new one under **Written by** in **Edit settings** → **Article**. A saved path to a program still called `gemini` is refused: clear it or set the Antigravity CLI path in Settings → Keys & services.
|
|
45
45
|
|
|
46
46
|
## Row states
|
|
47
47
|
|
|
48
48
|
| State | Meaning |
|
|
49
49
|
|---|---|
|
|
50
50
|
| **Ready** | Found, new enough and not reporting a sign-in problem. |
|
|
51
|
-
| **Needs
|
|
51
|
+
| **Needs fixing** | Found, but something is wrong (for example an old Codex version). The detail says what. |
|
|
52
52
|
| **Not found** | Not on PATH, or not at the saved path. |
|
|
53
53
|
|
|
54
54
|
## Set the executable path
|
|
55
55
|
|
|
56
56
|
Slopify finds each tool on PATH, the list of folders your terminal searches. Set a path when the tool is installed somewhere else, or you have more than one copy.
|
|
57
57
|
|
|
58
|
-
1. Pick the tool in **Settings →
|
|
58
|
+
1. Pick the tool in **Settings → Keys & services**.
|
|
59
59
|
2. Choose **Change path**.
|
|
60
60
|
3. In **Executable path**, type the absolute path to the program file, without quotes or arguments. Leave it blank to go back to finding it on PATH.
|
|
61
61
|
4. Choose **Save path**. Slopify checks the tool answers before saving; a folder or a missing file is refused with the reason.
|
|
@@ -67,7 +67,7 @@ Slopify finds each tool on PATH, the list of folders your terminal searches. Set
|
|
|
67
67
|
|
|
68
68
|
## Check the sign-in
|
|
69
69
|
|
|
70
|
-
**Check
|
|
70
|
+
**Check every service** (Settings → Keys & services) asks each tool:
|
|
71
71
|
|
|
72
72
|
- **Installed**: found, with its version.
|
|
73
73
|
- **Signed in**: Slopify asks each CLI directly. If one is signed out, the line tells you the command to run (`claude auth login`, `codex login` or `agy`). For Antigravity CLI it runs `agy models`: a model list means signed in, "Please sign in" means signed out (run `agy`), and anything else is shown as unknown.
|
|
@@ -79,20 +79,20 @@ If a run fails because a tool's sign-in expired, the project offers **Copy sign-
|
|
|
79
79
|
|
|
80
80
|
## Pick a model and thinking level
|
|
81
81
|
|
|
82
|
-
A CLI's model list comes from the tool installed on your computer, not from the model catalogue. On
|
|
82
|
+
A CLI's model list comes from the tool installed on your computer, not from the model catalogue. On Create:
|
|
83
83
|
|
|
84
|
-
1. Pick the tool as the
|
|
84
|
+
1. Pick the tool as the **Service** under **Written by** (or Codex CLI under **Images by** for images).
|
|
85
85
|
2. Pick a **Text model** from the list. The refresh button beside it (**Refresh models**) asks the tool for its current list; **Custom ID** lets you type a model the list does not show.
|
|
86
|
-
3. If the model offers it, pick **Thinking** (text) or **Effort** (images). **Model default** leaves it to the tool. The levels come from the model: for example Low, Medium, High and
|
|
86
|
+
3. If the model offers it, pick **Thinking** (text) or **Effort** (images). **Model default** leaves it to the tool. The levels come from the model: for example Low, Medium, High and Extra high. Higher levels can write or draw more carefully, but take longer and use more of your plan.
|
|
87
87
|
|
|
88
|
-
If a model is missing when a run reaches it, the run stops with "The chosen model is not available in the AI command-line tool on your computer." Update the tool, or pick another model in **Edit
|
|
88
|
+
If a model is missing when a run reaches it, the run stops with "The chosen model is not available in the AI command-line tool on your computer." Update the tool, or pick another model in **Edit settings** (under **Written by**, or **Images by** for Codex images), then **Continue the run**. A thinking level the model does not support stops the same way.
|
|
89
89
|
|
|
90
90
|
## Codex images
|
|
91
91
|
|
|
92
92
|
Codex draws each image as its own job.
|
|
93
93
|
|
|
94
94
|
- **Codex default** runs with the tool's own defaults: no model and no effort on the command line.
|
|
95
|
-
- Any other model is one of Codex's own models (the same list
|
|
95
|
+
- Any other model is one of Codex's own models (the same list Codex shows as a writing service), run with the **Effort** you pick. With a chosen model or effort, or a reference picture to match, the agent may look at its work and redraw it.
|
|
96
96
|
- Each image may take several minutes; the time limit for one image is 30 minutes.
|
|
97
97
|
- Up to 4 images are drawn at once.
|
|
98
98
|
|
|
@@ -106,16 +106,16 @@ The CLIs bill your subscription, which gives a share of a 5-hour and a weekly al
|
|
|
106
106
|
- **Codex** is asked before and after each call. It reports whole percents, so a short run can show "under 1%".
|
|
107
107
|
- **Antigravity** reports none.
|
|
108
108
|
|
|
109
|
-
You see these
|
|
109
|
+
You see these in a project's cost breakdown (**See the breakdown** on its cost line) and on Home under **This week**. See [Costs-and-Run-Cost](Costs-and-Run-Cost).
|
|
110
110
|
|
|
111
111
|
When a tool says its allowance is used up, the run does not fail:
|
|
112
112
|
|
|
113
113
|
1. Slopify stores the reset time the tool gave (per tool, not per project).
|
|
114
114
|
2. Every call to that tool, from any project, waits until 2 minutes after the reset. Without a stated time, it checks again every 30 minutes.
|
|
115
|
-
3. The project's status reads **Waiting for limits**, with a line such as "Waiting for Codex limits (resets at 14:00)." The same words show on Home's Running
|
|
115
|
+
3. The project's status reads **Waiting for limits**, with a line such as "Waiting for Codex limits (resets at 14:00)." The same words show on Home's Running, the Projects row and the Calendar.
|
|
116
116
|
4. When the reset passes, the run continues by itself.
|
|
117
117
|
|
|
118
|
-
Other
|
|
118
|
+
Other services are not held up: a project whose narration uses a key carries on while its Codex images wait. The wait survives a restart; a project that was waiting when Slopify stopped resumes on the next start and waits again. A project you paused or canceled meanwhile is left alone.
|
|
119
119
|
|
|
120
120
|
Antigravity's quota is handled the same way ("Slopify waits and tries again later by itself").
|
|
121
121
|
|
|
@@ -125,14 +125,14 @@ A container cannot use the CLIs installed on your computer directly. On Linux, t
|
|
|
125
125
|
|
|
126
126
|
1. Install and sign in to the CLIs on your computer first.
|
|
127
127
|
2. Run the Docker installer (`npx @gentbajko/slopify@latest --docker`). If a CLI is found, it asks once whether to set up the helper. `--accept-host-cli` approves without asking; `--host-cli=off` skips it.
|
|
128
|
-
3. In Settings →
|
|
128
|
+
3. In Settings → Keys & services the tools show **Managed on host**. Their path cannot be changed there.
|
|
129
129
|
|
|
130
130
|
What to know:
|
|
131
131
|
|
|
132
132
|
- The helper runs as you, as the user service `slopify-cli-bridge.service`, and talks to the container over a private socket. No network port is opened and no sign-in is copied into Docker.
|
|
133
133
|
- After you install, update or remove a CLI, run the Docker installer again so Slopify sees the change.
|
|
134
134
|
- Run cost and plan-limit shares work the same as without Docker. An older helper may not pass them through, and the cost page then says the share is not known.
|
|
135
|
-
- With the helper off, Settings shows the CLIs as unavailable. API
|
|
135
|
+
- With the helper off, Settings shows the CLIs as unavailable. Services with an API key still work.
|
|
136
136
|
|
|
137
137
|
Check or turn off the helper:
|
|
138
138
|
|
|
@@ -141,17 +141,17 @@ systemctl --user status slopify-cli-bridge.service
|
|
|
141
141
|
systemctl --user disable --now slopify-cli-bridge.service
|
|
142
142
|
```
|
|
143
143
|
|
|
144
|
-
Turning it off leaves Docker, your
|
|
144
|
+
Turning it off leaves Docker, your keyed services, your data and your host sign-ins as they are. See [Docker](Docker).
|
|
145
145
|
|
|
146
146
|
## Tips
|
|
147
147
|
|
|
148
|
-
- A CLI step on
|
|
149
|
-
- If your plan runs out often, split the work: keep
|
|
150
|
-
- Use **Check
|
|
148
|
+
- A CLI step on Create's estimate reads "$0 on your plan · ~$X via API": what the same tokens would cost with a key.
|
|
149
|
+
- If your plan runs out often, split the work: keep the writing on a CLI and put images on a keyed service, or the other way round.
|
|
150
|
+
- Use **Check every service** after updating a CLI; a newer tool can drop a model your template still names.
|
|
151
151
|
|
|
152
152
|
## Related pages
|
|
153
153
|
|
|
154
|
-
- [
|
|
154
|
+
- [Keys and Services](Providers-and-Keys)
|
|
155
155
|
- [Models](Models)
|
|
156
156
|
- [Costs-and-Run-Cost](Costs-and-Run-Cost)
|
|
157
157
|
- [Docker](Docker)
|