@gentbajko/slopify 3.25.9 → 4.0.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 +30 -0
- package/dist/adapters/alignment/cache.js +3 -5
- package/dist/adapters/alignment/ctc.js +1 -2
- package/dist/adapters/alignment/lock.js +4 -3
- package/dist/adapters/alignment/multilingual.js +0 -3
- package/dist/adapters/alignment/prefetch.js +2 -1
- package/dist/adapters/alignment/worker.js +2 -1
- package/dist/adapters/explain.js +0 -3
- package/dist/adapters/ffmpeg.js +19 -1
- package/dist/adapters/host-cli/index.js +19 -3
- package/dist/adapters/http-failure.js +53 -0
- package/dist/adapters/image/codex.js +36 -4
- package/dist/adapters/image/fal.js +17 -50
- package/dist/adapters/image/google.js +31 -55
- package/dist/adapters/image/openai.js +20 -47
- package/dist/adapters/image/reference.js +36 -0
- package/dist/adapters/image/replicate.js +18 -47
- package/dist/adapters/interest/search-interest.js +113 -0
- package/dist/adapters/key-probes.js +53 -2
- package/dist/adapters/llm/antigravity.js +4 -0
- package/dist/adapters/llm/assistant-cli.js +388 -0
- package/dist/adapters/llm/child-session.js +83 -0
- package/dist/adapters/llm/claude-code-models.js +51 -98
- package/dist/adapters/llm/claude-code.js +13 -2
- package/dist/adapters/llm/codex-limits.js +27 -68
- package/dist/adapters/llm/codex-server-models.js +61 -109
- package/dist/adapters/llm/codex.js +12 -1
- package/dist/adapters/llm/openrouter.js +10 -47
- package/dist/adapters/llm/run-cli.js +6 -22
- package/dist/adapters/llm/sse-lines.js +5 -1
- package/dist/adapters/model-access.js +37 -0
- package/dist/adapters/tts/cartesia.js +15 -46
- package/dist/adapters/tts/elevenlabs.js +20 -52
- package/dist/adapters/tts/gemini.js +19 -40
- package/dist/adapters/tts/inworld-async.js +4 -1
- package/dist/adapters/tts/inworld.js +3 -0
- package/dist/adapters/tts/openai.js +20 -50
- package/dist/assets/models-lifecycle.yaml +6 -0
- package/dist/assistant-wiring.js +60 -0
- package/dist/autopilot-timers.js +59 -0
- package/dist/catalog/lifecycle.js +52 -0
- package/dist/catalog/propose.js +204 -0
- package/dist/catalog/registry.js +3 -1
- package/dist/catalog/schema.js +2 -0
- package/dist/catalog/store.js +39 -0
- package/dist/edge/autostart/native.js +3 -5
- package/dist/edge/autostart/prompt.js +2 -1
- package/dist/edge/autostart/service.js +3 -4
- package/dist/edge/cli-args.js +3 -4
- package/dist/edge/cli.js +2 -1
- package/dist/edge/docker-install/apply.js +11 -11
- package/dist/edge/docker-install/run.js +2 -1
- package/dist/edge/docker-install/state.js +4 -3
- package/dist/edge/docker-install/tree.js +2 -4
- package/dist/edge/docker-install/volume.js +3 -2
- package/dist/edge/docker.js +5 -4
- package/dist/edge/events/hub.js +5 -5
- package/dist/edge/http/actions.js +4 -25
- package/dist/edge/http/actor.js +5 -3
- package/dist/edge/http/app.js +85 -10
- package/dist/edge/http/assistant.js +127 -0
- package/dist/edge/http/audio-preview.js +4 -11
- package/dist/edge/http/auditions.js +1 -1
- package/dist/edge/http/autostart.js +5 -5
- package/dist/edge/http/backups.js +1 -3
- package/dist/edge/http/channel-memory.js +7 -20
- package/dist/edge/http/channels.js +13 -26
- package/dist/edge/http/checkpoints.js +33 -17
- package/dist/edge/http/design-templates.js +167 -0
- package/dist/edge/http/designs.js +178 -0
- package/dist/edge/http/diagnostics.js +5 -1
- package/dist/edge/http/document-themes.js +2 -11
- package/dist/edge/http/draft-files.js +1 -3
- package/dist/edge/http/draft-problem.js +4 -9
- package/dist/edge/http/drafts.js +3 -3
- package/dist/edge/http/entries.js +6 -9
- package/dist/edge/http/extension-auth.js +27 -0
- package/dist/edge/http/external-reviews.js +12 -11
- package/dist/edge/http/files.js +5 -11
- package/dist/edge/http/folder-location.js +1 -4
- package/dist/edge/http/fonts.js +0 -7
- package/dist/edge/http/home.js +38 -13
- package/dist/edge/http/host-cli.js +18 -8
- package/dist/edge/http/ids.js +8 -0
- package/dist/edge/http/keywords.js +35 -0
- package/dist/edge/http/model-checks.js +132 -0
- package/dist/edge/http/narration-peaks.js +5 -13
- package/dist/edge/http/onboarding.js +1 -14
- package/dist/edge/http/open-folder.js +5 -10
- package/dist/edge/http/outputs.js +4 -12
- package/dist/edge/http/patch-notes.js +4 -10
- package/dist/edge/http/planning.js +2 -6
- package/dist/edge/http/problem.js +2 -4
- package/dist/edge/http/project-create.js +1 -0
- package/dist/edge/http/project-templates.js +29 -17
- package/dist/edge/http/projects.js +2 -12
- package/dist/edge/http/prompts.js +7 -14
- package/dist/edge/http/pronunciations.js +1 -2
- package/dist/edge/http/providers.js +39 -24
- package/dist/edge/http/refusal.js +29 -0
- package/dist/edge/http/remote-access.js +40 -0
- package/dist/edge/http/remote-guard.js +71 -0
- package/dist/edge/http/request-guard.js +91 -0
- package/dist/edge/http/reviews.js +11 -24
- package/dist/edge/http/revision-files.js +5 -10
- package/dist/edge/http/revisions.js +10 -15
- package/dist/edge/http/run-cost.js +1 -2
- package/dist/edge/http/same-page.js +80 -0
- package/dist/edge/http/schedules.js +59 -32
- package/dist/edge/http/settings.js +22 -13
- package/dist/edge/http/social.js +282 -0
- package/dist/edge/http/staging.js +2 -4
- package/dist/edge/http/storage-files.js +5 -7
- package/dist/edge/http/storage.js +23 -23
- package/dist/edge/http/studio.js +23 -50
- package/dist/edge/http/style-preview.js +1 -4
- package/dist/edge/http/subtitles.js +3 -17
- package/dist/edge/http/template-studio.js +433 -0
- package/dist/edge/http/trash.js +10 -11
- package/dist/edge/http/tutorial.js +1 -2
- package/dist/edge/http/tutorials.js +5 -11
- package/dist/edge/http/update.js +7 -13
- package/dist/edge/http/waveform.js +3 -6
- package/dist/edge/http/youtube-edits.js +2 -3
- package/dist/edge/mcp/assistant.js +279 -0
- package/dist/edge/mcp/attribution.js +8 -2
- package/dist/edge/mcp/call-route.js +11 -2
- package/dist/edge/mcp/excluded-routes.js +25 -0
- package/dist/edge/mcp/http.js +11 -2
- package/dist/edge/mcp/instructions.js +5 -3
- package/dist/edge/mcp/library-tools.js +1 -0
- package/dist/edge/mcp/route-tool.js +35 -15
- package/dist/edge/mcp/server.js +13 -8
- package/dist/edge/mcp/tools-channels.js +2 -0
- package/dist/edge/mcp/tools-designs.js +165 -0
- package/dist/edge/mcp/tools-library.js +14 -2
- package/dist/edge/mcp/tools-planning.js +23 -4
- package/dist/edge/mcp/tools-projects.js +28 -1
- package/dist/edge/mcp/tools-runs.js +14 -1
- package/dist/edge/mcp/tools-schedules.js +28 -4
- package/dist/edge/mcp/tools-setup.js +61 -3
- package/dist/edge/mcp/tools-social.js +46 -0
- package/dist/edge/mcp/tools-storage.js +8 -0
- package/dist/edge/mcp/tools-template-studio.js +166 -0
- package/dist/edge/mcp/tools.js +6 -0
- package/dist/edge/remote-access/cli.js +71 -0
- package/dist/edge/remote-access/model.js +32 -0
- package/dist/edge/remote-access/rules.js +207 -0
- package/dist/edge/remote-access/service.js +203 -0
- package/dist/edge/remote-access/store.js +44 -0
- package/dist/edge/remote-access/views.js +43 -0
- package/dist/extension/slopify-studio-chrome.zip +0 -0
- package/dist/extension/slopify-studio-firefox.zip +0 -0
- package/dist/host-cli/install.js +2 -2
- package/dist/host-cli/open-folder.js +2 -2
- package/dist/host-cli/paths.js +2 -4
- package/dist/host-cli/server.js +4 -4
- package/dist/host-cli/service.js +3 -3
- package/dist/host-cli/status.js +7 -7
- package/dist/kernel/db/migrations/0063-model-checks.sql +20 -0
- package/dist/kernel/db/migrations/0064-template-studio.sql +46 -0
- package/dist/kernel/db/migrations/0065-promise-and-assistant-reviews.sql +58 -0
- package/dist/kernel/db/migrations/0066-assistant.sql +44 -0
- package/dist/kernel/db/migrations/0067-autopilot.sql +6 -0
- package/dist/kernel/db/migrations/0068-topic-scores.sql +17 -0
- package/dist/kernel/db/migrations/0070-designs.sql +53 -0
- package/dist/kernel/db/migrations/0071-design-usage.sql +33 -0
- package/dist/kernel/db/migrations/0072-design-templates.sql +33 -0
- package/dist/kernel/db/migrations/0073-schedule-design-kind.sql +5 -0
- package/dist/kernel/db/migrations/0074-template-bakeoffs.sql +44 -0
- package/dist/kernel/db/migrations/0075-social-posts.sql +25 -0
- package/dist/kernel/db/migrations/0076-channel-social.sql +5 -0
- package/dist/kernel/db/migrations/0077-post-prompts.sql +17 -0
- package/dist/kernel/db/tx.js +2 -4
- package/dist/kernel/errors.js +27 -1
- package/dist/kernel/json-answer.js +15 -0
- package/dist/kernel/lock.js +4 -9
- package/dist/kernel/paths.js +8 -0
- package/dist/kernel/ports/assistant-cli.js +4 -0
- package/dist/kernel/ports/host-cli.js +3 -0
- package/dist/kernel/ports/interest.js +4 -0
- package/dist/kernel/ports/languages.js +0 -3
- package/dist/kernel/ports/model.js +2 -0
- package/dist/kernel/runner/attempt.js +3 -1
- package/dist/kernel/runner/audition.js +3 -1
- package/dist/kernel/runner/checkpoint-authority.js +2 -1
- package/dist/kernel/runner/index.js +8 -10
- package/dist/kernel/runner/model-access.js +40 -0
- package/dist/kernel/runner/model-check.js +153 -0
- package/dist/kernel/runner/providers.js +15 -10
- package/dist/kernel/runner/standalone.js +9 -5
- package/dist/main.js +334 -19
- package/dist/model-checks.js +166 -0
- package/dist/patch-notes/4.0.0.md +51 -0
- package/dist/patch-notes/index.json +6 -0
- package/dist/slices/admission/heads.js +2 -1
- package/dist/slices/admission/repo.js +2 -1
- package/dist/slices/admission/rules.js +43 -8
- package/dist/slices/admission/schema.js +8 -0
- package/dist/slices/admission/stage-writes.js +0 -3
- package/dist/slices/admission/start.js +48 -31
- package/dist/slices/admission/substitute.js +7 -4
- package/dist/slices/agents/attention-posts.js +31 -0
- package/dist/slices/agents/attention-templates.js +33 -0
- package/dist/slices/agents/attention.js +10 -1
- package/dist/slices/agents/connection.js +2 -6
- package/dist/slices/agents/model.js +6 -3
- package/dist/slices/agents/repo.js +15 -7
- package/dist/slices/article/continuation.js +0 -63
- package/dist/slices/article/plain.js +3 -2
- package/dist/slices/article/segments.js +2 -62
- package/dist/slices/assistant/access.js +1 -0
- package/dist/slices/assistant/asks.js +80 -0
- package/dist/slices/assistant/cost.js +54 -0
- package/dist/slices/assistant/gate.js +222 -0
- package/dist/slices/assistant/model.js +79 -0
- package/dist/slices/assistant/names.js +23 -0
- package/dist/slices/assistant/prompt.js +74 -0
- package/dist/slices/assistant/repo.js +74 -0
- package/dist/slices/assistant/tokens.js +18 -0
- package/dist/slices/assistant/turn.js +342 -0
- package/dist/slices/autopilot/buffer.js +76 -0
- package/dist/slices/autopilot/model.js +14 -0
- package/dist/slices/autopilot/notify.js +27 -0
- package/dist/slices/autopilot/repo.js +34 -0
- package/dist/slices/autopilot/signals.js +194 -0
- package/dist/slices/autopilot/warnings.js +85 -0
- package/dist/slices/autopilot/weekly.js +100 -0
- package/dist/slices/backups/files.js +2 -6
- package/dist/slices/backups/repo.js +3 -15
- package/dist/slices/backups/service.js +4 -3
- package/dist/slices/channels/cast-images.js +2 -1
- package/dist/slices/channels/model.js +0 -1
- package/dist/slices/channels/rebrand.js +0 -3
- package/dist/slices/channels/repo.js +4 -0
- package/dist/slices/channels/schema.js +6 -0
- package/dist/slices/channels/service.js +10 -0
- package/dist/slices/channels/videos.js +2 -14
- package/dist/slices/checkpoints/index.js +0 -1
- package/dist/slices/control/providers.js +0 -26
- package/dist/slices/designs/artwork.js +66 -0
- package/dist/slices/designs/budget.js +23 -0
- package/dist/slices/designs/calls.js +124 -0
- package/dist/slices/designs/check-context.js +38 -0
- package/dist/slices/designs/check-text.js +43 -0
- package/dist/slices/designs/fields.js +88 -0
- package/dist/slices/designs/frames.js +34 -0
- package/dist/slices/designs/gate.js +128 -0
- package/dist/slices/designs/layout.js +293 -0
- package/dist/slices/designs/model.js +35 -0
- package/dist/slices/designs/pdf-job.js +23 -0
- package/dist/slices/designs/pdf-pages.js +30 -0
- package/dist/slices/designs/pdf-runner.js +68 -0
- package/dist/slices/designs/pdf-worker.js +24 -0
- package/dist/slices/designs/picture-size.js +79 -0
- package/dist/slices/designs/pictures.js +55 -0
- package/dist/slices/designs/quote.js +24 -0
- package/dist/slices/designs/render-args.js +36 -0
- package/dist/slices/designs/render-input.js +59 -0
- package/dist/slices/designs/render.js +215 -0
- package/dist/slices/designs/repo.js +101 -0
- package/dist/slices/designs/requests.js +80 -0
- package/dist/slices/designs/runner.js +239 -0
- package/dist/slices/designs/sample-repo.js +91 -0
- package/dist/slices/designs/sample.js +222 -0
- package/dist/slices/designs/schema.js +87 -0
- package/dist/slices/designs/sections.js +44 -0
- package/dist/slices/designs/service.js +233 -0
- package/dist/slices/designs/steps-repo.js +102 -0
- package/dist/slices/designs/studio-kind.js +39 -0
- package/dist/slices/designs/template-checks.js +44 -0
- package/dist/slices/designs/template-make.js +118 -0
- package/dist/slices/designs/templates.js +218 -0
- package/dist/slices/document/fonts.js +2 -1
- package/dist/slices/episodes/summarize.js +5 -1
- package/dist/slices/estimate/index.js +45 -7
- package/dist/slices/fonts/catalog.js +3 -2
- package/dist/slices/fonts/discovery.js +3 -2
- package/dist/slices/fonts/files.js +2 -6
- package/dist/slices/fonts/index.js +0 -1
- package/dist/slices/fonts/sfnt.js +45 -10
- package/dist/slices/images/appearance.js +6 -15
- package/dist/slices/images/scenes.js +100 -27
- package/dist/slices/keywords/digest.js +84 -0
- package/dist/slices/keywords/facts.js +92 -0
- package/dist/slices/keywords/fill.js +80 -0
- package/dist/slices/keywords/model.js +75 -0
- package/dist/slices/keywords/rules.js +187 -0
- package/dist/slices/keywords/schema.js +28 -0
- package/dist/slices/keywords/specs.js +260 -0
- package/dist/slices/keywords/values.js +74 -0
- package/dist/slices/library/lint.js +29 -12
- package/dist/slices/library/model.js +2 -0
- package/dist/slices/library/photorealistic.js +4 -12
- package/dist/slices/library/rename.js +9 -7
- package/dist/slices/library/save.js +14 -10
- package/dist/slices/library/slots.js +37 -11
- package/dist/slices/library/transfer.js +2 -1
- package/dist/slices/library/used-by.js +25 -10
- package/dist/slices/loudness/loudnorm.js +51 -11
- package/dist/slices/model-checks/cost.js +61 -0
- package/dist/slices/model-checks/listing.js +124 -0
- package/dist/slices/model-checks/model.js +20 -0
- package/dist/slices/model-checks/record.js +32 -0
- package/dist/slices/model-checks/repo.js +120 -0
- package/dist/slices/model-checks/rules.js +112 -0
- package/dist/slices/model-checks/run.js +143 -0
- package/dist/slices/model-checks/service.js +198 -0
- package/dist/slices/model-upkeep/switch.js +70 -25
- package/dist/slices/model-upkeep/usage.js +26 -11
- package/dist/slices/narration/breaths.js +2 -1
- package/dist/slices/narration/clean-pieces.js +2 -1
- package/dist/slices/narration/concat.js +2 -1
- package/dist/slices/narration/peaks.js +3 -4
- package/dist/slices/narration/pronunciation-chunks.js +4 -3
- package/dist/slices/narration/pronunciation.js +3 -2
- package/dist/slices/narration/steering.js +4 -3
- package/dist/slices/notifications/notifier.js +4 -3
- package/dist/slices/notifications/rules.js +5 -2
- package/dist/slices/notifications/settings.js +4 -12
- package/dist/slices/notifications/subject.js +2 -1
- package/dist/slices/onboarding/copy.js +2 -1
- package/dist/slices/onboarding/first-run.js +3 -2
- package/dist/slices/onboarding/full-video.js +3 -2
- package/dist/slices/onboarding/install.js +4 -3
- package/dist/slices/onboarding/model.js +0 -16
- package/dist/slices/onboarding/packs.js +22 -22
- package/dist/slices/onboarding/quick-short.js +2 -1
- package/dist/slices/onboarding/sample.js +0 -1
- package/dist/slices/onboarding/state.js +2 -11
- package/dist/slices/onboarding/template.js +2 -1
- package/dist/slices/patch-notes/library.js +3 -2
- package/dist/slices/play-drafts/convert.js +4 -0
- package/dist/slices/play-drafts/review-inputs.js +8 -2
- package/dist/slices/play-drafts/review.js +5 -4
- package/dist/slices/play-drafts/schema.js +4 -0
- package/dist/slices/project-templates/from-project.js +9 -2
- package/dist/slices/project-templates/repo.js +6 -1
- package/dist/slices/project-templates/setup.js +8 -0
- package/dist/slices/project-templates/stand-ins.js +22 -0
- package/dist/slices/rebuild/activity.js +2 -0
- package/dist/slices/rebuild/admission-repo.js +3 -2
- package/dist/slices/rebuild/narration-history.js +4 -5
- package/dist/slices/rebuild/narration-retry.js +2 -1
- package/dist/slices/rebuild/preview-details.js +27 -9
- package/dist/slices/rebuild/preview-plan.js +3 -8
- package/dist/slices/rebuild/recipe-build.js +8 -3
- package/dist/slices/rebuild/recipe-describe.js +3 -3
- package/dist/slices/rebuild/recipe-digest.js +58 -0
- package/dist/slices/rebuild/recipe-input-schema.js +10 -0
- package/dist/slices/rebuild/recipe-keywords.js +71 -0
- package/dist/slices/rebuild/recipe-legacy.js +5 -1
- package/dist/slices/rebuild/recipe-model.js +4 -0
- package/dist/slices/rebuild/recipe-narration-text.js +2 -1
- package/dist/slices/rebuild/recipe-preparation.js +3 -3
- package/dist/slices/rebuild/recipe-reference.js +8 -4
- package/dist/slices/rebuild/recipe-reviews.js +27 -7
- package/dist/slices/rebuild/recipe-save.js +6 -3
- package/dist/slices/rebuild/recipe-scenes.js +40 -11
- package/dist/slices/rebuild/recipe-shorts.js +8 -8
- package/dist/slices/rebuild/recipe-social.js +46 -0
- package/dist/slices/rebuild/recipe-text.js +64 -20
- package/dist/slices/rebuild/recipe-validation.js +98 -28
- package/dist/slices/rebuild/recipe-visual.js +55 -16
- package/dist/slices/rebuild/recipe-work.js +15 -3
- package/dist/slices/rebuild/recipe-youtube.js +4 -3
- package/dist/slices/rebuild/recovery-selection.js +3 -1
- package/dist/slices/rebuild/recovery.js +3 -2
- package/dist/slices/rebuild/repo.js +4 -3
- package/dist/slices/rebuild/review-promise.js +25 -0
- package/dist/slices/rebuild/review-redo.js +2 -1
- package/dist/slices/rebuild/runtime-admission.js +4 -3
- package/dist/slices/rebuild/runtime-agent-review.js +11 -5
- package/dist/slices/rebuild/runtime-animate.js +9 -8
- package/dist/slices/rebuild/runtime-article.js +3 -2
- package/dist/slices/rebuild/runtime-checkpoints.js +5 -4
- package/dist/slices/rebuild/runtime-document.js +8 -11
- package/dist/slices/rebuild/runtime-export-bed.js +6 -11
- package/dist/slices/rebuild/runtime-export-edit.js +6 -15
- package/dist/slices/rebuild/runtime-export-inputs.js +7 -7
- package/dist/slices/rebuild/runtime-export-short.js +8 -4
- package/dist/slices/rebuild/runtime-export.js +34 -12
- package/dist/slices/rebuild/runtime-figure-card.js +2 -1
- package/dist/slices/rebuild/runtime-image.js +4 -9
- package/dist/slices/rebuild/runtime-instructions.js +2 -0
- package/dist/slices/rebuild/runtime-keywords.js +59 -0
- package/dist/slices/rebuild/runtime-lines.js +2 -11
- package/dist/slices/rebuild/runtime-local.js +14 -20
- package/dist/slices/rebuild/runtime-narration-publication.js +2 -5
- package/dist/slices/rebuild/runtime-narration-reuse.js +9 -13
- package/dist/slices/rebuild/runtime-narration-text.js +10 -15
- package/dist/slices/rebuild/runtime-pictures.js +2 -4
- package/dist/slices/rebuild/runtime-piece-label.js +10 -6
- package/dist/slices/rebuild/runtime-plan.js +31 -5
- package/dist/slices/rebuild/runtime-provider.js +60 -19
- package/dist/slices/rebuild/runtime-publication.js +10 -9
- package/dist/slices/rebuild/runtime-review.js +33 -28
- package/dist/slices/rebuild/runtime-run.js +4 -1
- package/dist/slices/rebuild/runtime-shorts.js +25 -45
- package/dist/slices/rebuild/runtime-store.js +3 -8
- package/dist/slices/rebuild/runtime-subtitles.js +16 -14
- package/dist/slices/rebuild/runtime-voices.js +8 -15
- package/dist/slices/rebuild/runtime-youtube.js +11 -29
- package/dist/slices/rebuild/transition-repo.js +6 -5
- package/dist/slices/rebuild/work-records.js +6 -0
- package/dist/slices/reruns/cascade.js +1 -2
- package/dist/slices/reruns/index.js +6 -9
- package/dist/slices/reviews/external-model.js +3 -0
- package/dist/slices/reviews/external-repo.js +2 -1
- package/dist/slices/reviews/external-rows.js +2 -3
- package/dist/slices/reviews/model.js +26 -8
- package/dist/slices/reviews/repo.js +2 -1
- package/dist/slices/reviews/rules.js +1 -0
- package/dist/slices/reviews/view.js +5 -2
- package/dist/slices/revisions/downloads.js +4 -5
- package/dist/slices/revisions/mutation-assets.js +7 -4
- package/dist/slices/revisions/mutation-cues.js +2 -5
- package/dist/slices/revisions/mutations.js +0 -1
- package/dist/slices/revisions/repo.js +3 -1
- package/dist/slices/revisions/schema.js +1 -0
- package/dist/slices/revisions/view.js +20 -0
- package/dist/slices/run-cost/limits.js +5 -2
- package/dist/slices/run-cost/meter.js +12 -5
- package/dist/slices/run-cost/panel.js +8 -74
- package/dist/slices/run-cost/plan-readings.js +127 -0
- package/dist/slices/run-cost/plan-use.js +169 -0
- package/dist/slices/run-cost/pricing.js +3 -3
- package/dist/slices/run-cost/week.js +32 -4
- package/dist/slices/schedules/agenda.js +11 -2
- package/dist/slices/schedules/by-date.js +42 -0
- package/dist/slices/schedules/calendar.js +5 -0
- package/dist/slices/schedules/design-agenda.js +36 -0
- package/dist/slices/schedules/design-check.js +41 -0
- package/dist/slices/schedules/design-run.js +126 -0
- package/dist/slices/schedules/next-run.js +62 -0
- package/dist/slices/schedules/prepare.js +18 -2
- package/dist/slices/schedules/repo.js +24 -16
- package/dist/slices/schedules/rescore.js +35 -0
- package/dist/slices/schedules/scheduler.js +13 -3
- package/dist/slices/schedules/schema.js +54 -3
- package/dist/slices/schedules/score.js +107 -0
- package/dist/slices/schedules/service.js +57 -24
- package/dist/slices/schedules/topic-list.js +15 -0
- package/dist/slices/schedules/topic-scores.js +104 -0
- package/dist/slices/schedules/topics.js +48 -36
- package/dist/slices/settings/cli-paths.js +4 -6
- package/dist/slices/settings/first-run.js +2 -11
- package/dist/slices/settings/model-reach.js +3 -17
- package/dist/slices/settings/model.js +10 -0
- package/dist/slices/settings/playback.js +33 -22
- package/dist/slices/settings/probe-read.js +16 -0
- package/dist/slices/settings/repo.js +33 -0
- package/dist/slices/settings/tutorial.js +2 -2
- package/dist/slices/settings/voices.js +4 -12
- package/dist/slices/shorts/pick.js +2 -15
- package/dist/slices/shorts/prompts.js +2 -13
- package/dist/slices/shorts/render.js +3 -0
- package/dist/slices/social/channel.js +44 -0
- package/dist/slices/social/defaults.js +17 -0
- package/dist/slices/social/due.js +36 -0
- package/dist/slices/social/extension.js +18 -0
- package/dist/slices/social/fit.js +115 -0
- package/dist/slices/social/limits.js +186 -0
- package/dist/slices/social/links.js +21 -0
- package/dist/slices/social/media.js +131 -0
- package/dist/slices/social/model.js +75 -0
- package/dist/slices/social/notice.js +19 -0
- package/dist/slices/social/pack.js +176 -0
- package/dist/slices/social/post-prompt.js +55 -0
- package/dist/slices/social/queue.js +31 -0
- package/dist/slices/social/repo.js +133 -0
- package/dist/slices/social/reshape.js +97 -0
- package/dist/slices/social/rules.js +5 -0
- package/dist/slices/social/schema.js +70 -0
- package/dist/slices/social/serve.js +51 -0
- package/dist/slices/social/service.js +128 -0
- package/dist/slices/social/settings.js +21 -0
- package/dist/slices/social/sources.js +114 -0
- package/dist/slices/social/state.js +110 -0
- package/dist/slices/social/text.js +149 -0
- package/dist/slices/storage/arrange.js +3 -1
- package/dist/slices/storage/assets.js +8 -10
- package/dist/slices/storage/backup-designs.js +74 -0
- package/dist/slices/storage/backup-export.js +8 -3
- package/dist/slices/storage/backup-format.js +10 -0
- package/dist/slices/storage/backup-import.js +20 -2
- package/dist/slices/storage/delete-project.js +6 -5
- package/dist/slices/storage/files-location.js +6 -8
- package/dist/slices/storage/layout.js +13 -2
- package/dist/slices/storage/model.js +3 -0
- package/dist/slices/storage/portable.js +16 -11
- package/dist/slices/storage/schema.js +2 -0
- package/dist/slices/storage/staging-refs.js +2 -1
- package/dist/slices/storage/staging.js +6 -9
- package/dist/slices/storage/trim.js +10 -3
- package/dist/slices/studio/calendar.js +4 -1
- package/dist/slices/studio/pick.js +2 -11
- package/dist/slices/studio/plan.js +6 -15
- package/dist/slices/studio/queue.js +3 -14
- package/dist/slices/studio/releases.js +26 -0
- package/dist/slices/studio/settings.js +14 -51
- package/dist/slices/studio/videos.js +12 -0
- package/dist/slices/style-preview/render.js +34 -13
- package/dist/slices/style-preview/schema.js +13 -0
- package/dist/slices/style-preview/service.js +2 -1
- package/dist/slices/style-preview/settings.js +33 -5
- package/dist/slices/telemetry/collector-client.js +2 -4
- package/dist/slices/telemetry/flush.js +2 -4
- package/dist/slices/telemetry/model.js +0 -7
- package/dist/slices/telemetry/record.js +10 -3
- package/dist/slices/template-studio/anatomy-view.js +118 -0
- package/dist/slices/template-studio/anatomy.js +73 -0
- package/dist/slices/template-studio/bakeoff-make.js +225 -0
- package/dist/slices/template-studio/bakeoff-repo.js +83 -0
- package/dist/slices/template-studio/bakeoff.js +333 -0
- package/dist/slices/template-studio/budget.js +103 -0
- package/dist/slices/template-studio/checks.js +273 -0
- package/dist/slices/template-studio/document-sample.js +56 -0
- package/dist/slices/template-studio/edit.js +15 -0
- package/dist/slices/template-studio/for-all.js +161 -0
- package/dist/slices/template-studio/free-video.js +31 -0
- package/dist/slices/template-studio/free.js +176 -0
- package/dist/slices/template-studio/gate.js +40 -0
- package/dist/slices/template-studio/hidden.js +20 -0
- package/dist/slices/template-studio/jobs.js +79 -0
- package/dist/slices/template-studio/loudness.js +111 -0
- package/dist/slices/template-studio/make.js +40 -0
- package/dist/slices/template-studio/model.js +54 -0
- package/dist/slices/template-studio/pack-import.js +253 -0
- package/dist/slices/template-studio/pack-samples.js +60 -0
- package/dist/slices/template-studio/pack.js +205 -0
- package/dist/slices/template-studio/paid-view.js +66 -0
- package/dist/slices/template-studio/paid.js +348 -0
- package/dist/slices/template-studio/quote.js +148 -0
- package/dist/slices/template-studio/ready.js +104 -0
- package/dist/slices/template-studio/repo.js +134 -0
- package/dist/slices/template-studio/reviews-path.js +58 -0
- package/dist/slices/template-studio/runner.js +84 -0
- package/dist/slices/template-studio/save.js +236 -0
- package/dist/slices/template-studio/say-it-as.js +248 -0
- package/dist/slices/template-studio/schedule-gate.js +34 -0
- package/dist/slices/template-studio/schedule-price.js +30 -0
- package/dist/slices/template-studio/schedule-sample.js +87 -0
- package/dist/slices/template-studio/schema.js +218 -0
- package/dist/slices/template-studio/scope.js +124 -0
- package/dist/slices/template-studio/sections.js +176 -0
- package/dist/slices/template-studio/service.js +173 -0
- package/dist/slices/template-studio/status.js +174 -0
- package/dist/slices/template-studio/topic.js +223 -0
- package/dist/slices/trash/designs.js +76 -0
- package/dist/slices/trash/model.js +13 -4
- package/dist/slices/trash/service.js +52 -2
- package/dist/slices/tutorials/library.js +3 -2
- package/dist/slices/video/ass-text.js +17 -0
- package/dist/slices/video/audio-export.js +2 -64
- package/dist/slices/video/encoding.js +160 -0
- package/dist/slices/video/ffmpeg.js +23 -21
- package/dist/slices/video/figure-card.js +1 -15
- package/dist/slices/video/slideshow.js +43 -18
- package/dist/slices/voices/palette.js +0 -3
- package/dist/slices/youtube/answer.js +2 -15
- package/dist/slices/youtube/edits-repo.js +2 -11
- package/dist/tutorials/Agents-MCP.md +2 -1
- package/dist/tutorials/Assistant.md +42 -0
- package/dist/tutorials/Channels.md +2 -0
- package/dist/tutorials/Home-and-Projects.md +19 -0
- package/dist/tutorials/Install.md +1 -0
- package/dist/tutorials/Keywords-Slopify-Fills.md +87 -0
- package/dist/tutorials/Models.md +33 -1
- package/dist/tutorials/Notifications.md +6 -0
- package/dist/tutorials/Play-Images.md +4 -0
- package/dist/tutorials/Posting-to-Social-Sites.md +121 -0
- package/dist/tutorials/Prompts.md +10 -6
- package/dist/tutorials/Remote-Access.md +63 -0
- package/dist/tutorials/Reviews-and-Checkpoints.md +13 -2
- package/dist/tutorials/Schedules.md +21 -1
- package/dist/tutorials/Settings-Reference.md +50 -8
- package/dist/tutorials/Template-Studio.md +125 -0
- package/dist/tutorials/Templates.md +3 -0
- package/dist/tutorials/_Sidebar.md +5 -0
- package/dist/updater/work-in-progress.js +42 -12
- package/dist/web/assets/index-CkAI_2E7.css +1 -0
- package/dist/web/assets/index-DcD0cdfo.js +334 -0
- package/dist/web/assets/{pdf-T7MCwjIv.js → pdf-TODRwF31.js} +1 -1
- package/dist/web/index.html +2 -2
- package/package.json +5 -1
- package/dist/slices/article/provided-entries.js +0 -66
- package/dist/slices/article/run.js +0 -145
- package/dist/slices/images/run.js +0 -234
- package/dist/slices/narration/legacy-plan.js +0 -58
- package/dist/slices/narration/run.js +0 -270
- package/dist/slices/rebuild/runtime-actions.js +0 -90
- package/dist/slices/research/run.js +0 -224
- package/dist/slices/subtitles/prepare.js +0 -194
- package/dist/slices/subtitles/transcript.js +0 -34
- package/dist/slices/thumbnail/run.js +0 -219
- package/dist/slices/video/audio-inputs.js +0 -33
- package/dist/slices/video/reuse-audio.js +0 -62
- package/dist/slices/video/run.js +0 -78
- package/dist/slices/video/write-export.js +0 -158
- package/dist/slices/video/write-subtitles.js +0 -59
- package/dist/web/assets/index-BadsoORk.css +0 -1
- package/dist/web/assets/index-D8LqNsCl.js +0 -326
|
@@ -17,7 +17,7 @@ import { keyStatus, removeProviderKey, saveProviderKey } from "../../slices/sett
|
|
|
17
17
|
import { isUncataloguedProvider, providerById, providerIds } from "../../slices/settings/model.js";
|
|
18
18
|
import { createModelCatalog } from "../../slices/settings/models.js";
|
|
19
19
|
import { providerStatuses } from "../../slices/settings/readiness.js";
|
|
20
|
-
import { onInvalid, problem
|
|
20
|
+
import { onInvalid, problem } from "./problem.js";
|
|
21
21
|
const providerParam = z.object({ id: z.enum(providerIds) });
|
|
22
22
|
const healthQuery = z.object({ provider: z.enum(providerIds).optional() });
|
|
23
23
|
// ceiling: no format check is allowed on a key, so the only thing said about the value is
|
|
@@ -26,7 +26,7 @@ const healthQuery = z.object({ provider: z.enum(providerIds).optional() });
|
|
|
26
26
|
const pathBody = z.object({ path: z.string().max(cliPathMaxLength) });
|
|
27
27
|
const keyBody = z.object({ key: z.string().min(1).max(4096) });
|
|
28
28
|
const modelId = z.string().min(1).max(200);
|
|
29
|
-
const switchBody = z
|
|
29
|
+
export const switchBody = z
|
|
30
30
|
.object({
|
|
31
31
|
kind: z.enum(usageKinds),
|
|
32
32
|
id: z.string().min(1).max(64),
|
|
@@ -53,6 +53,15 @@ export function providerRoutes(deps) {
|
|
|
53
53
|
report: (provider) => deps.log.write("warn", "model-catalog", { detail: `Could not load models for ${provider}` }),
|
|
54
54
|
});
|
|
55
55
|
const upkeep = () => ({ ...deps, uuid: deps.drafts?.uuid ?? randomUUID });
|
|
56
|
+
// A picker opening reads the key's own model list again when it is due; never waited for.
|
|
57
|
+
const relist = (id) => {
|
|
58
|
+
if (deps.autoModelChecks !== true)
|
|
59
|
+
return;
|
|
60
|
+
void deps.modelListing?.refresh(id).catch((error) => {
|
|
61
|
+
deps.log.write("warn", "model-checks.listing", { detail: `${id}: ${String(error)}` });
|
|
62
|
+
});
|
|
63
|
+
};
|
|
64
|
+
const standingsOf = (id, models) => deps.modelChecks?.standings(id, models);
|
|
56
65
|
return (new Hono()
|
|
57
66
|
// The step lists Settings → Providers shows beside each key field.
|
|
58
67
|
.get("/key-guides", (c) => c.json({ guides: keyGuides }))
|
|
@@ -70,7 +79,7 @@ export function providerRoutes(deps) {
|
|
|
70
79
|
// or a sign-in fix-it's).
|
|
71
80
|
.post("/health", zValidator("query", healthQuery, onInvalid), async (c) => {
|
|
72
81
|
if (deps.fetch === undefined)
|
|
73
|
-
return problem(c, { status: 503,
|
|
82
|
+
return problem(c, { status: 503, detail: noFetch });
|
|
74
83
|
const catalogue = deps.catalogue;
|
|
75
84
|
return c.json(await checkProviderHealth({
|
|
76
85
|
...readiness,
|
|
@@ -87,7 +96,6 @@ export function providerRoutes(deps) {
|
|
|
87
96
|
if (!deps.catalogue?.sync)
|
|
88
97
|
return problem(c, {
|
|
89
98
|
status: 503,
|
|
90
|
-
title: titleOf(503),
|
|
91
99
|
detail: "The model list (models.yaml) is not loaded yet. Wait a moment and reload the page; if it stays like this, restart Slopify.",
|
|
92
100
|
});
|
|
93
101
|
await deps.catalogue.sync();
|
|
@@ -97,21 +105,23 @@ export function providerRoutes(deps) {
|
|
|
97
105
|
.get("/catalogue/retired", (c) => {
|
|
98
106
|
c.header("Cache-Control", "no-store");
|
|
99
107
|
return c.json({
|
|
100
|
-
usages: deps.catalogue === undefined
|
|
108
|
+
usages: deps.catalogue === undefined
|
|
109
|
+
? []
|
|
110
|
+
: retiredModelUsage(deps.db, deps.catalogue.read(), deps.catalogue.lifecycle?.()),
|
|
101
111
|
});
|
|
102
112
|
})
|
|
103
113
|
.post("/catalogue/retired/switch", zValidator("json", switchBody, onInvalid), async (c) => {
|
|
104
114
|
if (deps.catalogue === undefined)
|
|
105
|
-
return problem(c, { status: 503,
|
|
115
|
+
return problem(c, { status: 503, detail: noFetch });
|
|
106
116
|
const result = await switchRetiredModel(upkeep(), deps.catalogue.read(), c.req.valid("json"));
|
|
107
117
|
if (!result.ok)
|
|
108
|
-
return problem(c, { status: 409,
|
|
118
|
+
return problem(c, { status: 409, detail: result.message });
|
|
109
119
|
return c.json(result);
|
|
110
120
|
})
|
|
111
121
|
.post("/catalogue/retired/switch-all", async (c) => {
|
|
112
122
|
if (deps.catalogue === undefined)
|
|
113
|
-
return problem(c, { status: 503,
|
|
114
|
-
return c.json(await switchAllRetired(upkeep(), deps.catalogue.read()));
|
|
123
|
+
return problem(c, { status: 503, detail: noFetch });
|
|
124
|
+
return c.json(await switchAllRetired(upkeep(), deps.catalogue.read(), deps.catalogue.lifecycle?.()));
|
|
115
125
|
})
|
|
116
126
|
.get("/catalogue", (c) => c.json(deps.catalogue?.status() ?? {
|
|
117
127
|
warning: "The model list (models.yaml) is not loaded yet. Wait a moment and reload the page; if it stays like this, restart Slopify.",
|
|
@@ -120,7 +130,6 @@ export function providerRoutes(deps) {
|
|
|
120
130
|
if (!deps.catalogue)
|
|
121
131
|
return problem(c, {
|
|
122
132
|
status: 503,
|
|
123
|
-
title: titleOf(503),
|
|
124
133
|
detail: "The model list (models.yaml) is not loaded yet. Wait a moment and reload the page; if it stays like this, restart Slopify.",
|
|
125
134
|
});
|
|
126
135
|
try {
|
|
@@ -129,7 +138,6 @@ export function providerRoutes(deps) {
|
|
|
129
138
|
catch {
|
|
130
139
|
return problem(c, {
|
|
131
140
|
status: 502,
|
|
132
|
-
title: titleOf(502),
|
|
133
141
|
detail: "Slopify could not download the latest model list. Your current list is still in use; check your internet connection and try Refresh again.",
|
|
134
142
|
});
|
|
135
143
|
}
|
|
@@ -155,30 +163,36 @@ export function providerRoutes(deps) {
|
|
|
155
163
|
const { id } = c.req.valid("param");
|
|
156
164
|
// Animate images lists the provider's image-to-video models, which only the catalogue
|
|
157
165
|
// names.
|
|
158
|
-
|
|
166
|
+
relist(id);
|
|
167
|
+
if (c.req.query("video") === "1") {
|
|
168
|
+
const videos = deps.catalogue === undefined ? [] : videoModelsOf(deps.catalogue.read(), id);
|
|
159
169
|
return c.json({
|
|
160
|
-
models:
|
|
170
|
+
models: videos,
|
|
161
171
|
allowsCustom: false,
|
|
172
|
+
standings: standingsOf(id, videos),
|
|
162
173
|
});
|
|
163
|
-
|
|
174
|
+
}
|
|
175
|
+
if (deps.catalogue && !isUncataloguedProvider(id)) {
|
|
176
|
+
const models = deps.catalogue
|
|
177
|
+
.models(id, providerById(id).family)
|
|
178
|
+
.map((m) => ({ ...m, ...catalogueModelInfo(m) }));
|
|
164
179
|
return c.json({
|
|
165
|
-
models
|
|
166
|
-
.models(id, providerById(id).family)
|
|
167
|
-
.map((m) => ({ ...m, ...catalogueModelInfo(m) })),
|
|
180
|
+
models,
|
|
168
181
|
allowsCustom: false,
|
|
169
182
|
notice: "Curated active models from models.yaml. Edit it locally or refresh in Settings.",
|
|
170
183
|
warning: deps.catalogue.status().warning ?? undefined,
|
|
184
|
+
standings: standingsOf(id, models),
|
|
171
185
|
});
|
|
186
|
+
}
|
|
172
187
|
const result = await catalog.get(id, providerById(id).family, c.req.query("refresh") === "1");
|
|
173
188
|
c.header("Cache-Control", "no-store");
|
|
174
|
-
return c.json(result);
|
|
189
|
+
return c.json({ ...result, standings: standingsOf(id, result.models) });
|
|
175
190
|
})
|
|
176
191
|
.put("/:id/path", zValidator("param", providerParam, onInvalid), zValidator("json", pathBody, onInvalid), async (c) => {
|
|
177
192
|
const result = await saveCliPath(readiness, c.req.valid("param").id, c.req.valid("json").path);
|
|
178
193
|
if (!result.ok)
|
|
179
194
|
return problem(c, {
|
|
180
195
|
status: 400,
|
|
181
|
-
title: titleOf(400),
|
|
182
196
|
detail: result.message,
|
|
183
197
|
extensions: { fields: [{ field: "path", message: result.message }] },
|
|
184
198
|
});
|
|
@@ -192,6 +206,11 @@ export function providerRoutes(deps) {
|
|
|
192
206
|
return refusal(c, id, result.reason);
|
|
193
207
|
}
|
|
194
208
|
catalog.invalidate(id);
|
|
209
|
+
// A new key: read its model list and check what is free to check, in the background.
|
|
210
|
+
if (deps.autoModelChecks === true)
|
|
211
|
+
void deps.modelChecks?.automatic("key-added", id).catch((error) => {
|
|
212
|
+
deps.log.write("warn", "model-checks", { detail: `${id}: ${String(error)}` });
|
|
213
|
+
});
|
|
195
214
|
// The response says a key is stored and shows the mask, never the value.
|
|
196
215
|
return c.json(keyStatus(keys, id));
|
|
197
216
|
})
|
|
@@ -203,13 +222,12 @@ export function providerRoutes(deps) {
|
|
|
203
222
|
if (providerById(id).auth !== "key")
|
|
204
223
|
return refusal(c, id, "cli-provider");
|
|
205
224
|
if (deps.fetch === undefined)
|
|
206
|
-
return problem(c, { status: 503,
|
|
225
|
+
return problem(c, { status: 503, detail: noFetch });
|
|
207
226
|
const raw = await c.req.json().catch(() => undefined);
|
|
208
227
|
const candidate = raw === undefined ? undefined : keyBody.safeParse(raw);
|
|
209
228
|
if (candidate !== undefined && !candidate.success)
|
|
210
229
|
return problem(c, {
|
|
211
230
|
status: 400,
|
|
212
|
-
title: titleOf(400),
|
|
213
231
|
detail: `The pasted ${providerById(id).displayName} key could not be tested: it is empty or longer than any key. Paste the whole key in Settings → Providers → ${providerById(id).displayName}, then choose Test again.`,
|
|
214
232
|
});
|
|
215
233
|
return c.json(await testProviderKey({ db: deps.db, clock: deps.clock, fetch: deps.fetch, probes: keyProbes }, id, candidate?.data.key));
|
|
@@ -229,7 +247,6 @@ function refusal(c, id, reason) {
|
|
|
229
247
|
if (reason === "cli-provider") {
|
|
230
248
|
return problem(c, {
|
|
231
249
|
status: 400,
|
|
232
|
-
title: titleOf(400),
|
|
233
250
|
detail: providerById(id).auth === "local"
|
|
234
251
|
? `${name} is your computer's own speech, so it has no API key to save. It works as soon as a speech program is installed.`
|
|
235
252
|
: `${name} signs in through its own command-line tool, so it has no API key to save here. Sign in with that tool on your computer instead.`,
|
|
@@ -238,13 +255,11 @@ function refusal(c, id, reason) {
|
|
|
238
255
|
if (reason === "absent") {
|
|
239
256
|
return problem(c, {
|
|
240
257
|
status: 404,
|
|
241
|
-
title: titleOf(404),
|
|
242
258
|
detail: `There is no saved API key for ${name}, so there is nothing to remove.`,
|
|
243
259
|
});
|
|
244
260
|
}
|
|
245
261
|
return problem(c, {
|
|
246
262
|
status: 400,
|
|
247
|
-
title: titleOf(400),
|
|
248
263
|
detail: "This API key cannot be saved yet. Paste the key and try again.",
|
|
249
264
|
extensions: {
|
|
250
265
|
fields: [{ field: "key", message: "Paste the API key from your provider's account page." }],
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { problem } from "./problem.js";
|
|
2
|
+
export function refusal(c, reason, rules) {
|
|
3
|
+
return problem(c, {
|
|
4
|
+
status: rules.statuses[reason] ?? rules.otherwise,
|
|
5
|
+
detail: rules.detail,
|
|
6
|
+
extensions: { reason, ...rules.extensions },
|
|
7
|
+
});
|
|
8
|
+
}
|
|
9
|
+
export function revisionRequired(c) {
|
|
10
|
+
return problem(c, {
|
|
11
|
+
status: 409,
|
|
12
|
+
detail: "This change is now made from the Edit tab. Choose Edit project, save your change, then use Choose what to remake (in the project's More menu).",
|
|
13
|
+
extensions: { reason: "revision-required" },
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
// A build without its bundled files says so in plain words; anything else is the app's error handler's.
|
|
17
|
+
export function bundleMissing(c, error, Missing) {
|
|
18
|
+
if (!(error instanceof Missing))
|
|
19
|
+
throw error;
|
|
20
|
+
return problem(c, { status: 500, detail: error.message });
|
|
21
|
+
}
|
|
22
|
+
export async function jsonBody(c) {
|
|
23
|
+
try {
|
|
24
|
+
return await c.req.json();
|
|
25
|
+
}
|
|
26
|
+
catch {
|
|
27
|
+
return undefined;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { zValidator } from "@hono/zod-validator";
|
|
2
|
+
import { Hono } from "hono";
|
|
3
|
+
import { z } from "zod";
|
|
4
|
+
import { actorOf } from "./actor.js";
|
|
5
|
+
import { onInvalid, problem } from "./problem.js";
|
|
6
|
+
import { viaProxy } from "./same-page.js";
|
|
7
|
+
const body = z.object({
|
|
8
|
+
enabled: z.boolean(),
|
|
9
|
+
httpsPort: z.number().int().min(1).max(65535).optional(),
|
|
10
|
+
});
|
|
11
|
+
// Settings → General → Remote access: Slopify's page on the person's other devices through
|
|
12
|
+
// Tailscale serve. Reading it is open to agents (get_remote_access); turning it on or off is
|
|
13
|
+
// the person's alone, from this computer.
|
|
14
|
+
export function remoteAccessRoutes(service) {
|
|
15
|
+
const unavailable = "Remote access can't be set from this Slopify. Start it with npx @gentbajko/slopify and try Settings → General → Remote access again.";
|
|
16
|
+
return new Hono()
|
|
17
|
+
.get("/", async (c) => {
|
|
18
|
+
if (service === undefined)
|
|
19
|
+
return problem(c, { status: 404, detail: unavailable });
|
|
20
|
+
c.header("Cache-Control", "no-store");
|
|
21
|
+
const view = await service.view();
|
|
22
|
+
return c.json(viaProxy(c.req.raw.headers) ? { ...view, changeable: false } : view);
|
|
23
|
+
})
|
|
24
|
+
.put("/", zValidator("json", body, onInvalid), async (c) => {
|
|
25
|
+
if (service === undefined)
|
|
26
|
+
return problem(c, { status: 404, detail: unavailable });
|
|
27
|
+
if (actorOf(c) !== "person")
|
|
28
|
+
return problem(c, {
|
|
29
|
+
status: 403,
|
|
30
|
+
detail: "Remote access is the person's to turn on or off, in Settings → General → Remote access. Agents and the assistant can only read it (get_remote_access).",
|
|
31
|
+
});
|
|
32
|
+
if (viaProxy(c.req.raw.headers))
|
|
33
|
+
return problem(c, {
|
|
34
|
+
status: 403,
|
|
35
|
+
detail: "Remote access is turned on or off only on the computer Slopify runs on. Open Settings → General → Remote access there, at http://127.0.0.1 and Slopify's port.",
|
|
36
|
+
});
|
|
37
|
+
const { enabled, httpsPort } = c.req.valid("json");
|
|
38
|
+
return c.json(await service.set(enabled, httpsPort));
|
|
39
|
+
});
|
|
40
|
+
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { problem } from "./problem.js";
|
|
2
|
+
import { viaProxy } from "./same-page.js";
|
|
3
|
+
const id = "[^/]+";
|
|
4
|
+
export const hostOnlyWrites = [
|
|
5
|
+
{
|
|
6
|
+
method: "PUT|DELETE",
|
|
7
|
+
path: new RegExp(`^/api/providers/${id}/key/?$`),
|
|
8
|
+
what: "Provider keys are saved and removed (Settings → Providers)",
|
|
9
|
+
},
|
|
10
|
+
{
|
|
11
|
+
method: "PUT",
|
|
12
|
+
path: new RegExp(`^/api/providers/${id}/path/?$`),
|
|
13
|
+
what: "A CLI's path is set (Settings → Providers)",
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
method: "PUT",
|
|
17
|
+
path: /^\/api\/storage\/import\/?$/,
|
|
18
|
+
what: "A backup is imported (Settings → Backup & storage)",
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
method: "POST",
|
|
22
|
+
path: new RegExp(`^/api/storage/(?:cleanup|old-versions/delete|projects/${id}/(?:old-versions/delete|keep-outputs))/?$`),
|
|
23
|
+
what: "Clean-ups that delete files are run (Settings → Backup & storage)",
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
method: "POST",
|
|
27
|
+
path: /^\/api\/storage\/files\/move\/?$/,
|
|
28
|
+
what: "The Files folder is moved (Settings → Backup & storage)",
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
method: "PUT",
|
|
32
|
+
path: /^\/api\/backups\/?$/,
|
|
33
|
+
what: "The backup folder is set (Settings → Backup & storage)",
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
method: "POST|DELETE",
|
|
37
|
+
path: /^\/api\/update(?:\/activate)?\/?$/,
|
|
38
|
+
what: "Updates are installed (Settings → About)",
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
method: "PUT",
|
|
42
|
+
path: /^\/api\/settings\/autostart\/?$/,
|
|
43
|
+
what: "Start with this computer is turned on or off (Settings → General)",
|
|
44
|
+
},
|
|
45
|
+
];
|
|
46
|
+
export const otherAccount = "Slopify refused this request: over remote access it answers only the Tailscale account that turned remote access on. Sign in to Tailscale on this device with that account, or open Slopify on the computer it runs on.";
|
|
47
|
+
export const hostOnlySentence = (what) => `${what} only on the computer Slopify runs on, not over remote access: open Slopify there, at http://127.0.0.1 and Slopify's port, and do it there.`;
|
|
48
|
+
export function remoteGuard(remoteAccess) {
|
|
49
|
+
return async (c, next) => {
|
|
50
|
+
const headers = c.req.raw.headers;
|
|
51
|
+
// The agents' endpoints refuse every proxied request themselves, with their own sentence.
|
|
52
|
+
if (!viaProxy(headers) || /^\/mcp(?:\/|$)/.test(c.req.path))
|
|
53
|
+
return next();
|
|
54
|
+
const owner = remoteAccess?.owner();
|
|
55
|
+
const remote = remoteAccess?.remoteHost();
|
|
56
|
+
const throughServe = headers.has("tailscale-user-login") ||
|
|
57
|
+
(remote !== undefined &&
|
|
58
|
+
(headers.get("x-forwarded-host") === remote || headers.get("host") === remote));
|
|
59
|
+
if (owner !== undefined && throughServe && headers.get("tailscale-user-login") !== owner)
|
|
60
|
+
return problem(c, { status: 403, detail: otherAccount });
|
|
61
|
+
const path = c.req.path;
|
|
62
|
+
const write = hostOnlyWrites.find((rule) => rule.method.split("|").includes(c.req.method) && rule.path.test(path));
|
|
63
|
+
if (write !== undefined)
|
|
64
|
+
return problem(c, {
|
|
65
|
+
status: 403,
|
|
66
|
+
detail: hostOnlySentence(write.what),
|
|
67
|
+
extensions: { reason: "host-only" },
|
|
68
|
+
});
|
|
69
|
+
return next();
|
|
70
|
+
};
|
|
71
|
+
}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { isIP } from "node:net";
|
|
2
|
+
import { hostname } from "node:os";
|
|
3
|
+
import { actorOf } from "./actor.js";
|
|
4
|
+
import { problem } from "./problem.js";
|
|
5
|
+
import { forwardedHost, fromSlopifyPage } from "./same-page.js";
|
|
6
|
+
const nameOf = (host) => {
|
|
7
|
+
const trimmed = host.trim().toLowerCase();
|
|
8
|
+
if (trimmed.startsWith("["))
|
|
9
|
+
return trimmed.slice(1, trimmed.indexOf("]"));
|
|
10
|
+
const colon = trimmed.indexOf(":");
|
|
11
|
+
return colon === trimmed.lastIndexOf(":") && colon >= 0 ? trimmed.slice(0, colon) : trimmed;
|
|
12
|
+
};
|
|
13
|
+
const ownNames = () => {
|
|
14
|
+
const own = hostname().toLowerCase();
|
|
15
|
+
return own === "" ? [] : [own, `${own}.local`];
|
|
16
|
+
};
|
|
17
|
+
export function hostAllowed(host, rules) {
|
|
18
|
+
// An in-process request, or an HTTP/1.0 client, names no host.
|
|
19
|
+
if (host === undefined)
|
|
20
|
+
return true;
|
|
21
|
+
const name = nameOf(host);
|
|
22
|
+
if (name === "")
|
|
23
|
+
return false;
|
|
24
|
+
if (isIP(name) !== 0)
|
|
25
|
+
return true;
|
|
26
|
+
if (name === "localhost" || name.endsWith(".localhost"))
|
|
27
|
+
return true;
|
|
28
|
+
// This computer's tailnet name, also when the person serves Slopify through Tailscale
|
|
29
|
+
// themselves: a ts.net name only resolves inside a tailnet, never to someone's loopback.
|
|
30
|
+
if (name.endsWith(".ts.net"))
|
|
31
|
+
return true;
|
|
32
|
+
const named = [
|
|
33
|
+
rules.listenHost,
|
|
34
|
+
rules.remoteHost === undefined ? undefined : nameOf(rules.remoteHost),
|
|
35
|
+
...rules.extraHosts,
|
|
36
|
+
...ownNames(),
|
|
37
|
+
].flatMap((one) => (one === undefined || one.trim() === "" ? [] : [one.trim().toLowerCase()]));
|
|
38
|
+
return named.includes(name);
|
|
39
|
+
}
|
|
40
|
+
const formTypes = ["text/plain", "application/x-www-form-urlencoded", "multipart/form-data"];
|
|
41
|
+
const safeMethods = ["GET", "HEAD", "OPTIONS"];
|
|
42
|
+
// The paired extension's own routes: token-checked, with CORS for its origin alone.
|
|
43
|
+
const extensionRoutes = /^\/api\/(?:studio|social)\/ext(?:\/|$)/;
|
|
44
|
+
export function requestGuard(deps) {
|
|
45
|
+
return async (c, next) => {
|
|
46
|
+
if (actorOf(c) !== "person")
|
|
47
|
+
return next();
|
|
48
|
+
const rules = {
|
|
49
|
+
listenHost: deps.listenHost,
|
|
50
|
+
remoteHost: deps.remoteHost?.(),
|
|
51
|
+
extraHosts: deps.extraHosts,
|
|
52
|
+
};
|
|
53
|
+
const host = c.req.header("host");
|
|
54
|
+
const forwarded = forwardedHost(c);
|
|
55
|
+
const refused = !hostAllowed(host, rules)
|
|
56
|
+
? (host ?? "")
|
|
57
|
+
: forwarded !== undefined && !hostAllowed(forwarded, rules)
|
|
58
|
+
? forwarded
|
|
59
|
+
: undefined;
|
|
60
|
+
if (refused !== undefined) {
|
|
61
|
+
const shown = refused.replace(/[^\w.:[\]-]/g, "").slice(0, 100);
|
|
62
|
+
return problem(c, {
|
|
63
|
+
status: 403,
|
|
64
|
+
detail: `Slopify refused this request: it was addressed to ${shown}, a name this Slopify doesn't answer to, which is how a web page could reach it through your browser. Open Slopify at http://127.0.0.1 and its port. If you reach it by this name on purpose, start Slopify with SLOPIFY_ALLOWED_HOSTS=${nameOf(shown)}.`,
|
|
65
|
+
extensions: { reason: "foreign-host" },
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
if (safeMethods.includes(c.req.method) ||
|
|
69
|
+
!c.req.path.startsWith("/api/") ||
|
|
70
|
+
extensionRoutes.test(c.req.path))
|
|
71
|
+
return next();
|
|
72
|
+
const origin = c.req.header("origin");
|
|
73
|
+
if (origin !== undefined) {
|
|
74
|
+
if (fromSlopifyPage(c, deps.remoteHost))
|
|
75
|
+
return next();
|
|
76
|
+
return problem(c, {
|
|
77
|
+
status: 403,
|
|
78
|
+
detail: `Slopify refused this request: it came from a web page at another address (${origin.slice(0, 100)}). Changes are made only from Slopify's own page; open Slopify and try again there.`,
|
|
79
|
+
extensions: { reason: "foreign-origin" },
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
const type = (c.req.header("content-type") ?? "").split(";")[0]?.trim().toLowerCase() ?? "";
|
|
83
|
+
if (formTypes.includes(type) && c.req.header("x-slopify-client") === undefined)
|
|
84
|
+
return problem(c, {
|
|
85
|
+
status: 403,
|
|
86
|
+
detail: "Slopify refused this request: it looks like a form another web page sent. Slopify's page sends its changes itself; open Slopify and try again there. A script on this computer can send the header X-Slopify-Client: script.",
|
|
87
|
+
extensions: { reason: "form-request" },
|
|
88
|
+
});
|
|
89
|
+
return next();
|
|
90
|
+
};
|
|
91
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { zValidator } from "@hono/zod-validator";
|
|
2
2
|
import { Hono } from "hono";
|
|
3
|
-
import {
|
|
3
|
+
import { errorText } from "../../kernel/errors.js";
|
|
4
4
|
import { revisionControlSchema } from "../../slices/control/revision-control-schema.js";
|
|
5
5
|
import { recoverProject } from "../../slices/rebuild/recovery.js";
|
|
6
6
|
import { readRecovery } from "../../slices/rebuild/recovery-repo.js";
|
|
@@ -10,13 +10,11 @@ import { latestReviews } from "../../slices/reviews/view.js";
|
|
|
10
10
|
import { currentRevisionId } from "../../slices/revisions/repo.js";
|
|
11
11
|
import { getRevisionView } from "../../slices/revisions/view.js";
|
|
12
12
|
import { actorOf } from "./actor.js";
|
|
13
|
-
import {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
.regex(/^[0-9A-Za-z_-]+$/);
|
|
19
|
-
const projectParam = z.object({ id });
|
|
13
|
+
import { idParam, idString } from "./ids.js";
|
|
14
|
+
import { onInvalid, problem } from "./problem.js";
|
|
15
|
+
import { refusal } from "./refusal.js";
|
|
16
|
+
const id = idString;
|
|
17
|
+
const projectParam = idParam;
|
|
20
18
|
const verdictParam = projectParam.extend({ verdictId: id });
|
|
21
19
|
// The project page's review column: each reviewed item's latest verdict with its reasons,
|
|
22
20
|
// Overrule to accept a failed item as it is, and Redo to have it made again through the
|
|
@@ -24,13 +22,11 @@ const verdictParam = projectParam.extend({ verdictId: id });
|
|
|
24
22
|
export function reviewRoutes(deps) {
|
|
25
23
|
const gone = (c) => problem(c, {
|
|
26
24
|
status: 404,
|
|
27
|
-
title: titleOf(404),
|
|
28
25
|
detail: "This review no longer exists; the project may have been deleted or restored from a backup. Reload the project page.",
|
|
29
26
|
});
|
|
30
27
|
const decided = (c, verdict) => problem(c, {
|
|
31
28
|
status: 409,
|
|
32
|
-
|
|
33
|
-
detail: `This verdict was already decided: the item was ${verdict.action === "overruled" ? "kept as it is" : "sent to be made again"}${verdict.actionBy === "agent" ? " by an agent" : ""}, so nothing changed. Reload the project page (or call needs_attention) to see what waits now.`,
|
|
29
|
+
detail: `This verdict was already decided: the item was ${verdict.action === "overruled" ? "kept as it is" : "sent to be made again"}${verdict.actionBy === "agent" ? " by an agent" : verdict.actionBy === "assistant" ? " by the assistant" : ""}, so nothing changed. Reload the project page (or call needs_attention) to see what waits now.`,
|
|
34
30
|
extensions: { reason: "decided" },
|
|
35
31
|
});
|
|
36
32
|
const notify = (projectId) => {
|
|
@@ -40,7 +36,7 @@ export function reviewRoutes(deps) {
|
|
|
40
36
|
catch (error) {
|
|
41
37
|
deps.log.write("error", "review.notify", {
|
|
42
38
|
projectId,
|
|
43
|
-
detail:
|
|
39
|
+
detail: errorText(error),
|
|
44
40
|
});
|
|
45
41
|
}
|
|
46
42
|
};
|
|
@@ -62,7 +58,6 @@ export function reviewRoutes(deps) {
|
|
|
62
58
|
const { id: projectId, verdictId } = c.req.valid("param");
|
|
63
59
|
const busy = () => problem(c, {
|
|
64
60
|
status: 409,
|
|
65
|
-
title: titleOf(409),
|
|
66
61
|
detail: "This verdict can't be overruled: it passed, or Slopify has already started making the item again. Wait for the new version to land, then reload the project page and overrule its review if it fails again.",
|
|
67
62
|
extensions: { reason: "conflict" },
|
|
68
63
|
});
|
|
@@ -105,12 +100,10 @@ export function reviewRoutes(deps) {
|
|
|
105
100
|
if (deps.rebuild === undefined)
|
|
106
101
|
return problem(c, {
|
|
107
102
|
status: 503,
|
|
108
|
-
title: titleOf(503),
|
|
109
103
|
detail: "Redo is not ready yet because Slopify is still starting. Wait a moment, reload the page and try again.",
|
|
110
104
|
});
|
|
111
105
|
const already = () => problem(c, {
|
|
112
106
|
status: 409,
|
|
113
|
-
title: titleOf(409),
|
|
114
107
|
detail: "Slopify is already making this item again. Wait for it to land, then reload the project page.",
|
|
115
108
|
extensions: { reason: "conflict" },
|
|
116
109
|
});
|
|
@@ -138,11 +131,6 @@ export function reviewRoutes(deps) {
|
|
|
138
131
|
}
|
|
139
132
|
});
|
|
140
133
|
function refused(c, result, projectId) {
|
|
141
|
-
const status = result.reason === "no-project"
|
|
142
|
-
? 404
|
|
143
|
-
: result.reason === "invalid-selection" || result.reason === "invalid-edit"
|
|
144
|
-
? 400
|
|
145
|
-
: 409;
|
|
146
134
|
const lead = result.reason === "running"
|
|
147
135
|
? "This item, or work that depends on it, is still running. Wait for it to finish, or Pause the project, then use Redo again."
|
|
148
136
|
: result.reason === "readiness"
|
|
@@ -152,12 +140,11 @@ export function reviewRoutes(deps) {
|
|
|
152
140
|
: result.reason === "invalid-selection"
|
|
153
141
|
? "This item can't be made again."
|
|
154
142
|
: "Slopify couldn't make this item again.";
|
|
155
|
-
return
|
|
156
|
-
|
|
157
|
-
|
|
143
|
+
return refusal(c, result.reason, {
|
|
144
|
+
statuses: { "no-project": 404, "invalid-selection": 400, "invalid-edit": 400 },
|
|
145
|
+
otherwise: 409,
|
|
158
146
|
detail: [lead, ...(result.fields ?? []).map((row) => row.message)].join(" "),
|
|
159
147
|
extensions: {
|
|
160
|
-
reason: result.reason,
|
|
161
148
|
fields: result.fields ?? [],
|
|
162
149
|
currentRevisionId: currentRevisionId(deps.db, projectId) ?? null,
|
|
163
150
|
},
|
|
@@ -4,13 +4,11 @@ import { z } from "zod";
|
|
|
4
4
|
import { findRevisionDownload, revisionSetZip, } from "../../slices/revisions/downloads.js";
|
|
5
5
|
import { fileResponse } from "./byte-range.js";
|
|
6
6
|
import { replyForFolder } from "./folder-location.js";
|
|
7
|
-
import {
|
|
7
|
+
import { idString } from "./ids.js";
|
|
8
|
+
import { onInvalid, problem } from "./problem.js";
|
|
9
|
+
import { fromSlopifyPage } from "./same-page.js";
|
|
8
10
|
import { waveformAnswer } from "./waveform.js";
|
|
9
|
-
const id =
|
|
10
|
-
.string()
|
|
11
|
-
.min(1)
|
|
12
|
-
.max(64)
|
|
13
|
-
.regex(/^[0-9A-Za-z_-]+$/);
|
|
11
|
+
const id = idString;
|
|
14
12
|
const revisionParam = z.object({ projectId: id, revisionId: id });
|
|
15
13
|
const recordParam = revisionParam.extend({ recordId: id });
|
|
16
14
|
const folderParam = z.object({ id, revisionId: id, recordId: id });
|
|
@@ -66,11 +64,9 @@ export function revisionFileRoutes(deps) {
|
|
|
66
64
|
}
|
|
67
65
|
export function revisionFolderRoutes(deps) {
|
|
68
66
|
return new Hono().post("/:id/revisions/:revisionId/:recordId/open-folder", zValidator("param", folderParam, onInvalid), async (c) => {
|
|
69
|
-
|
|
70
|
-
if (origin !== undefined && origin !== new URL(c.req.url).origin)
|
|
67
|
+
if (!fromSlopifyPage(c, deps.remoteAccess?.remoteHost))
|
|
71
68
|
return problem(c, {
|
|
72
69
|
status: 403,
|
|
73
|
-
title: titleOf(403),
|
|
74
70
|
detail: "For your safety, folders can only be opened from the Slopify page itself. Open Slopify and try again there.",
|
|
75
71
|
});
|
|
76
72
|
const { id: projectId, revisionId, recordId } = c.req.valid("param");
|
|
@@ -83,7 +79,6 @@ export function revisionFolderRoutes(deps) {
|
|
|
83
79
|
function unavailable(c, reason) {
|
|
84
80
|
return problem(c, {
|
|
85
81
|
status: 404,
|
|
86
|
-
title: titleOf(404),
|
|
87
82
|
detail: reason === "missing-file"
|
|
88
83
|
? "This file was deleted or moved from the project folder. In the Edit tab, use Choose what to remake (in the project's More menu) to make it again."
|
|
89
84
|
: reason === "no-images"
|
|
@@ -10,16 +10,14 @@ import { ensureBaseline, restoreRevision, saveRevision } from "../../slices/revi
|
|
|
10
10
|
import { currentRevisionId, listRevisionHistory } from "../../slices/revisions/repo.js";
|
|
11
11
|
import { restoreRevisionSchema, saveRevisionSchema } from "../../slices/revisions/schema.js";
|
|
12
12
|
import { getRevisionView } from "../../slices/revisions/view.js";
|
|
13
|
-
import {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
.regex(/^[0-9A-Za-z_-]+$/);
|
|
19
|
-
const projectParam = z.object({ id });
|
|
13
|
+
import { idParam, idString } from "./ids.js";
|
|
14
|
+
import { onInvalid, problem } from "./problem.js";
|
|
15
|
+
import { refusal } from "./refusal.js";
|
|
16
|
+
const id = idString;
|
|
17
|
+
const projectParam = idParam;
|
|
20
18
|
const revisionParam = projectParam.extend({ revisionId: id });
|
|
21
|
-
const saveBody = saveRevisionSchema.extend({ idempotencyKey: z.uuid() });
|
|
22
|
-
const restoreBody = restoreRevisionSchema.extend({ idempotencyKey: z.uuid() });
|
|
19
|
+
export const saveBody = saveRevisionSchema.extend({ idempotencyKey: z.uuid() });
|
|
20
|
+
export const restoreBody = restoreRevisionSchema.extend({ idempotencyKey: z.uuid() });
|
|
23
21
|
const statuses = {
|
|
24
22
|
"no-project": 404,
|
|
25
23
|
"no-revision": 404,
|
|
@@ -45,13 +43,11 @@ const details = {
|
|
|
45
43
|
readiness: "Slopify cannot run this rebuild yet. Fix the problems listed, then try again.",
|
|
46
44
|
};
|
|
47
45
|
function refused(c, deps, projectId, value) {
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
title: titleOf(status),
|
|
46
|
+
return refusal(c, value.reason, {
|
|
47
|
+
statuses,
|
|
48
|
+
otherwise: 409,
|
|
52
49
|
detail: details[value.reason],
|
|
53
50
|
extensions: {
|
|
54
|
-
reason: value.reason,
|
|
55
51
|
currentRevisionId: currentRevisionId(deps.db, projectId) ?? null,
|
|
56
52
|
fields: value.fields ?? [],
|
|
57
53
|
},
|
|
@@ -60,7 +56,6 @@ function refused(c, deps, projectId, value) {
|
|
|
60
56
|
function unavailable(c) {
|
|
61
57
|
return problem(c, {
|
|
62
58
|
status: 503,
|
|
63
|
-
title: titleOf(503),
|
|
64
59
|
detail: "Rebuilding is not ready yet because Slopify is still starting. Wait a moment, reload the page and try again.",
|
|
65
60
|
});
|
|
66
61
|
}
|
|
@@ -3,7 +3,7 @@ import { Hono } from "hono";
|
|
|
3
3
|
import { z } from "zod";
|
|
4
4
|
import { projectExists } from "../../slices/admission/repo.js";
|
|
5
5
|
import { runCostOf } from "../../slices/run-cost/panel.js";
|
|
6
|
-
import { onInvalid, problem
|
|
6
|
+
import { onInvalid, problem } from "./problem.js";
|
|
7
7
|
const idParam = z.object({ id: z.string().min(1).max(64) });
|
|
8
8
|
// The project's Run cost tab and its "Waiting for … limits" line: what the run's provider
|
|
9
9
|
// calls used and cost, and any CLI plan limit a stage is waiting on. Read from the stored
|
|
@@ -17,7 +17,6 @@ export function runCostRoutes(deps) {
|
|
|
17
17
|
if (!projectExists(deps.db, id))
|
|
18
18
|
return problem(c, {
|
|
19
19
|
status: 404,
|
|
20
|
-
title: titleOf(404),
|
|
21
20
|
detail: "This project no longer exists. Go back to Projects to pick another.",
|
|
22
21
|
});
|
|
23
22
|
return c.json(runCostOf(deps.db, id));
|