@gentbajko/slopify 3.25.8 → 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/3.25.9.md +5 -0
- package/dist/patch-notes/4.0.0.md +51 -0
- package/dist/patch-notes/index.json +12 -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 +34 -50
- 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
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
// Renaming a Library prompt or intro/outro. Everything names the Library by name, so a rename
|
|
2
|
-
// rewrites the name where a future run reads it:
|
|
3
|
-
//
|
|
4
|
-
//
|
|
5
|
-
//
|
|
2
|
+
// rewrites the name where a future run reads it: every version of every template (its form and
|
|
3
|
+
// the copy it saved of the Library) and every Play draft still being edited. Every version, not
|
|
4
|
+
// only the newest: a gated template's schedules run its newest ready version (`template-studio/
|
|
5
|
+
// gate.ts`), Export pack carries that one, a version's samples stay current only while the
|
|
6
|
+
// versions since differ in what they changed, and a trashed template or an old version can be
|
|
7
|
+
// put back. The name is a pointer, so rewriting it changes nothing a version means. A project
|
|
8
|
+
// revision is the record of what its run used and keeps the old name; "Used by" finds it
|
|
9
|
+
// through `formerNames`.
|
|
6
10
|
import { z } from "zod";
|
|
7
11
|
import { renamedIn } from "./used-by.js";
|
|
8
12
|
const documentRow = z.object({ key: z.string(), version: z.number(), document_json: z.string() });
|
|
@@ -56,9 +60,7 @@ export function renameReferences(db, ref, id, to) {
|
|
|
56
60
|
if (ref.name === to)
|
|
57
61
|
return;
|
|
58
62
|
const templates = db
|
|
59
|
-
.prepare(
|
|
60
|
-
JOIN project_template_revisions r ON r.template_id=t.id AND r.version=t.head_version
|
|
61
|
-
WHERE t.deleted_at IS NULL`)
|
|
63
|
+
.prepare("SELECT template_id AS key,version,document_json FROM project_template_revisions")
|
|
62
64
|
.all()
|
|
63
65
|
.map((row) => documentRow.parse(row));
|
|
64
66
|
const updateTemplate = db.prepare("UPDATE project_template_revisions SET document_json=? WHERE template_id=? AND version=?");
|
|
@@ -8,8 +8,10 @@ import { lintEntry, lintPrompt } from "./lint.js";
|
|
|
8
8
|
import { entryCategories, promptKinds } from "./model.js";
|
|
9
9
|
import { renameReferences } from "./rename.js";
|
|
10
10
|
import { entryById, insertEntry, insertPrompt, promptById, replaceEntry, replacePrompt, trashEntry, trashPrompt, } from "./repo.js";
|
|
11
|
-
|
|
12
|
-
|
|
11
|
+
// Duplicate names the prompt it copies (`copyOf`): a copy of one saved before the `Slopify_`
|
|
12
|
+
// prefix was reserved keeps the names that one already has, as an edit of it would.
|
|
13
|
+
export function createPrompt(deps, draft, copyOf) {
|
|
14
|
+
const fields = lintPrompt(draft, copyOf === undefined ? [] : (promptById(deps.db, copyOf)?.slots ?? []));
|
|
13
15
|
if (fields.length > 0) {
|
|
14
16
|
return { ok: false, reason: "invalid", fields };
|
|
15
17
|
}
|
|
@@ -21,8 +23,10 @@ export function createPrompt(deps, draft) {
|
|
|
21
23
|
}, prompt);
|
|
22
24
|
}
|
|
23
25
|
// A save replaces the row whole and keeps what was saved as a new version (`history.ts`).
|
|
24
|
-
export function updatePrompt(deps, id, draft, restoredFrom
|
|
25
|
-
|
|
26
|
+
export function updatePrompt(deps, id, draft, restoredFrom,
|
|
27
|
+
// Slot names a restored version had, which it may keep like the stored prompt's.
|
|
28
|
+
restoredSlots = []) {
|
|
29
|
+
const fields = lintPrompt(draft, [...(promptById(deps.db, id)?.slots ?? []), ...restoredSlots]);
|
|
26
30
|
if (fields.length > 0) {
|
|
27
31
|
return { ok: false, reason: "invalid", fields };
|
|
28
32
|
}
|
|
@@ -47,7 +51,7 @@ export function restorePrompt(deps, id, version) {
|
|
|
47
51
|
const kind = promptKinds.find((one) => one === saved?.kind);
|
|
48
52
|
if (saved === undefined || kind === undefined || promptById(deps.db, id) === undefined)
|
|
49
53
|
return { ok: false, reason: "not-found" };
|
|
50
|
-
return updatePrompt(deps, id, { kind, name: saved.name, body: saved.body }, version);
|
|
54
|
+
return updatePrompt(deps, id, { kind, name: saved.name, body: saved.body }, version, detectSlots(saved.body).names);
|
|
51
55
|
}
|
|
52
56
|
// Delete moves the prompt to the trash with its history; Settings → Trash puts it back or
|
|
53
57
|
// removes it for good (`slices/trash`).
|
|
@@ -56,8 +60,8 @@ export function removePrompt(deps, id) {
|
|
|
56
60
|
? { ok: true, value: null }
|
|
57
61
|
: { ok: false, reason: "not-found" };
|
|
58
62
|
}
|
|
59
|
-
export function createEntry(deps, draft) {
|
|
60
|
-
const fields = lintEntry(draft);
|
|
63
|
+
export function createEntry(deps, draft, copyOf) {
|
|
64
|
+
const fields = lintEntry(draft, copyOf === undefined ? [] : (entryById(deps.db, copyOf)?.slots ?? []));
|
|
61
65
|
if (fields.length > 0) {
|
|
62
66
|
return { ok: false, reason: "invalid", fields };
|
|
63
67
|
}
|
|
@@ -68,8 +72,8 @@ export function createEntry(deps, draft) {
|
|
|
68
72
|
return true;
|
|
69
73
|
}, entry);
|
|
70
74
|
}
|
|
71
|
-
export function updateEntry(deps, id, draft, restoredFrom) {
|
|
72
|
-
const fields = lintEntry(draft);
|
|
75
|
+
export function updateEntry(deps, id, draft, restoredFrom, restoredSlots = []) {
|
|
76
|
+
const fields = lintEntry(draft, [...(entryById(deps.db, id)?.slots ?? []), ...restoredSlots]);
|
|
73
77
|
if (fields.length > 0) {
|
|
74
78
|
return { ok: false, reason: "invalid", fields };
|
|
75
79
|
}
|
|
@@ -95,7 +99,7 @@ export function restoreEntry(deps, id, version) {
|
|
|
95
99
|
saved.mode === null ||
|
|
96
100
|
entryById(deps.db, id) === undefined)
|
|
97
101
|
return { ok: false, reason: "not-found" };
|
|
98
|
-
return updateEntry(deps, id, { category, mode: saved.mode, name: saved.name, body: saved.body }, version);
|
|
102
|
+
return updateEntry(deps, id, { category, mode: saved.mode, name: saved.name, body: saved.body }, version, detectSlots(saved.body).names);
|
|
99
103
|
}
|
|
100
104
|
export function removeEntry(deps, id) {
|
|
101
105
|
return trashEntry(deps.db, id, deps.clock.now().toISOString())
|
|
@@ -4,8 +4,10 @@
|
|
|
4
4
|
// is the one that runs.
|
|
5
5
|
import { normaliseDraft, usesNarrationPreparation, usesReference, usesShorts, usesYoutubeDescription, } from "../admission/rules.js";
|
|
6
6
|
import { collectFields, render } from "../admission/substitute.js";
|
|
7
|
+
import { keywordUsesOf, placedProblems, textEntryProblems, } from "../keywords/rules.js";
|
|
7
8
|
import { reviewPromptKey } from "../reviews/model.js";
|
|
8
9
|
import { activeReviewStages } from "../reviews/rules.js";
|
|
10
|
+
import { socialPlatforms } from "../social/model.js";
|
|
9
11
|
import { usesScriptPrompt } from "../voices/model.js";
|
|
10
12
|
import { snapshotEntry, snapshotPrompt } from "./snapshot.js";
|
|
11
13
|
export function pickTemplates(db, input, snapshot) {
|
|
@@ -16,40 +18,63 @@ export function pickTemplates(db, input, snapshot) {
|
|
|
16
18
|
// entries. A prompt left selected on a stage set to Provide asks for no field.
|
|
17
19
|
const text = [];
|
|
18
20
|
const image = [];
|
|
21
|
+
// Each picked body with the field that picked it and where it runs, for the keyword checks.
|
|
22
|
+
const placed = [];
|
|
23
|
+
const body = (kind, name, field, into, key) => {
|
|
24
|
+
const at = into.length;
|
|
25
|
+
pickBody(db, kind, name, field, missing, into, key, snapshot);
|
|
26
|
+
const picked = into[at];
|
|
27
|
+
if (picked !== undefined)
|
|
28
|
+
placed.push({ field, place: kind, body: picked.body });
|
|
29
|
+
};
|
|
19
30
|
if (sources.article === "generate") {
|
|
20
|
-
body(
|
|
31
|
+
body(usesScriptPrompt(draft) ? "script" : "article", draft.articlePrompt, "articlePrompt", text, "article");
|
|
21
32
|
}
|
|
22
33
|
const intro = pickEntry(db, "intro", draft.intro, missing, snapshot);
|
|
23
34
|
if (usesNarrationPreparation(draft))
|
|
24
|
-
body(
|
|
35
|
+
body("narration", draft.narrationPrompt, "narrationPrompt", text, "narration");
|
|
25
36
|
const outro = pickEntry(db, "outro", draft.outro, missing, snapshot);
|
|
26
37
|
push(text, "intro", intro);
|
|
27
38
|
push(text, "outro", outro);
|
|
39
|
+
if (intro !== undefined)
|
|
40
|
+
placed.push({ field: "intro", place: "intro", body: intro.body });
|
|
41
|
+
if (outro !== undefined)
|
|
42
|
+
placed.push({ field: "outro", place: "outro", body: outro.body });
|
|
43
|
+
for (const entry of [intro, outro])
|
|
44
|
+
if (entry?.mode === "text")
|
|
45
|
+
missing.push(...textEntryProblems(entry.body, entry.category));
|
|
28
46
|
// None picked is the built-in prompt, which asks for no keyword.
|
|
29
47
|
if (usesYoutubeDescription(draft))
|
|
30
|
-
body(
|
|
48
|
+
body("description", draft.descriptionPrompt, "descriptionPrompt", text, "description");
|
|
31
49
|
// The Shorts step's two prompts; none picked is the built-in one, which asks for no keyword.
|
|
32
50
|
if (usesShorts(draft)) {
|
|
33
|
-
body(
|
|
34
|
-
body(
|
|
51
|
+
body("shorts", draft.shorts?.prompt, "shorts.prompt", text, "shorts");
|
|
52
|
+
body("image", draft.shorts?.imagePrompt, "shorts.imagePrompt", image, "shortsImage");
|
|
35
53
|
}
|
|
36
54
|
// Each reviewed stage's Review prompt; none picked is the stage's built-in one.
|
|
37
55
|
for (const stage of activeReviewStages(draft))
|
|
38
|
-
body(
|
|
56
|
+
body("review", draft.reviews?.stages[stage]?.prompt, `reviews.stages.${stage}.prompt`, text, reviewPromptKey(stage));
|
|
57
|
+
// Each site's post prompt, for the sites whose post is written by one; none picked is the
|
|
58
|
+
// built-in one, which asks for no keyword.
|
|
59
|
+
for (const platform of socialPlatforms) {
|
|
60
|
+
const choice = draft.social?.[platform];
|
|
61
|
+
if (choice?.text === "prompt")
|
|
62
|
+
body("post", choice.prompt, `social.${platform}.prompt`, text, `social.${platform}`);
|
|
63
|
+
}
|
|
39
64
|
// The establishing image's prompt: an image prompt like the others, its keywords filled the
|
|
40
65
|
// same way.
|
|
41
66
|
if (usesReference(draft) && draft.reference?.source === "prompt")
|
|
42
|
-
body(
|
|
67
|
+
body("image", draft.reference.prompt, "reference.prompt", image, "referencePrompt");
|
|
43
68
|
if (sources.images === "generate") {
|
|
44
69
|
for (const [index, picked] of draft.imagePrompts.entries()) {
|
|
45
70
|
const field = `imagePrompts.${String(index)}.name`;
|
|
46
|
-
body(
|
|
71
|
+
body("image", picked.name, field, image, `imagePrompts.${String(index)}`);
|
|
47
72
|
}
|
|
48
73
|
}
|
|
49
74
|
// Both Generate modes read the same thumbnail template; Prompt by LLM sends it as the
|
|
50
75
|
// instruction rather than to the image provider.
|
|
51
76
|
if (sources.thumbnail === "from_prompt" || sources.thumbnail === "prompt_by_llm") {
|
|
52
|
-
body(
|
|
77
|
+
body("thumbnail", draft.thumbnailPrompt, "thumbnailPrompt", image, "thumbnailPrompt");
|
|
53
78
|
}
|
|
54
79
|
const fields = collectFields(
|
|
55
80
|
// The title's keywords are asked for like a prompt's: it is filled when the run starts.
|
|
@@ -58,7 +83,8 @@ export function pickTemplates(db, input, snapshot) {
|
|
|
58
83
|
draft: withSavedModes(draft, intro, outro),
|
|
59
84
|
requiredSlots: fields.map((field) => field.name),
|
|
60
85
|
bodies: [...text, ...image],
|
|
61
|
-
missing,
|
|
86
|
+
missing: [...missing, ...placedProblems(placed, draft.values)],
|
|
87
|
+
keywordUses: keywordUsesOf(placed),
|
|
62
88
|
};
|
|
63
89
|
}
|
|
64
90
|
// Every picked body rendered once, with the trimmed values
|
|
@@ -70,7 +96,7 @@ export function renderPicked(picked, values) {
|
|
|
70
96
|
}
|
|
71
97
|
return rendered;
|
|
72
98
|
}
|
|
73
|
-
function
|
|
99
|
+
function pickBody(db, kind, name, field, missing, into, key, snapshot) {
|
|
74
100
|
// An unpicked prompt is admission's rule to state ("Pick an article prompt."), so this
|
|
75
101
|
// says nothing about it and the form marks the field once.
|
|
76
102
|
if (name === undefined || name.trim() === "") {
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
+
import { errorText } from "../../kernel/errors.js";
|
|
2
3
|
import { documentThemeNameMax } from "../document/model.js";
|
|
3
4
|
import { documentThemeSchema } from "../document/theme-schema.js";
|
|
4
5
|
import { narrationAliasSchema } from "../narration/aliases-schema.js";
|
|
@@ -202,7 +203,7 @@ export async function importEach(items, options) {
|
|
|
202
203
|
break;
|
|
203
204
|
}
|
|
204
205
|
catch (error) {
|
|
205
|
-
reason =
|
|
206
|
+
reason = errorText(error);
|
|
206
207
|
break;
|
|
207
208
|
}
|
|
208
209
|
}
|
|
@@ -5,6 +5,8 @@
|
|
|
5
5
|
// `snapshot.ts` use.
|
|
6
6
|
import { z } from "zod";
|
|
7
7
|
import { liveProject } from "../admission/repo.js";
|
|
8
|
+
import { runnableVersion } from "../template-studio/gate.js";
|
|
9
|
+
import { gatesOf, notTemplateSample } from "../template-studio/repo.js";
|
|
8
10
|
function record(value) {
|
|
9
11
|
return typeof value === "object" && value !== null && !Array.isArray(value)
|
|
10
12
|
? value
|
|
@@ -40,6 +42,8 @@ function slotsIn(value, ref) {
|
|
|
40
42
|
return field(shorts, "prompt");
|
|
41
43
|
case "review":
|
|
42
44
|
return Object.values(record(record(value.reviews)?.stages) ?? {}).flatMap((stage) => field(record(stage), "prompt"));
|
|
45
|
+
case "post":
|
|
46
|
+
return Object.values(record(value.social) ?? {}).flatMap((one) => field(record(one), "prompt"));
|
|
43
47
|
case "image":
|
|
44
48
|
return [
|
|
45
49
|
...(Array.isArray(value.imagePrompts) ? value.imagePrompts : []).flatMap((one) => field(record(one), "name")),
|
|
@@ -106,18 +110,28 @@ export function usedBy(db, ref, formerNames = []) {
|
|
|
106
110
|
const parsed = templateRow.parse(row);
|
|
107
111
|
return matches(formOf(parsed.document_json)) ? [{ id: parsed.id, name: parsed.name }] : [];
|
|
108
112
|
});
|
|
109
|
-
// A schedule runs its template
|
|
110
|
-
//
|
|
113
|
+
// A schedule runs the version of its template the studio gate lets it run
|
|
114
|
+
// (`template-studio/schedule-gate.ts`), which can be older than the newest one.
|
|
115
|
+
const runsOf = new Map();
|
|
116
|
+
const formRun = (templateId, head) => {
|
|
117
|
+
if (!runsOf.has(templateId)) {
|
|
118
|
+
const version = runnableVersion(gatesOf(db, templateId), head) ?? head;
|
|
119
|
+
const row = db
|
|
120
|
+
.prepare("SELECT document_json FROM project_template_revisions WHERE template_id=? AND version=?")
|
|
121
|
+
.get(templateId, version);
|
|
122
|
+
runsOf.set(templateId, row === undefined ? undefined : formOf(String(row.document_json)));
|
|
123
|
+
}
|
|
124
|
+
return runsOf.get(templateId);
|
|
125
|
+
};
|
|
111
126
|
const schedules = db
|
|
112
|
-
.prepare(`SELECT s.id,s.name,s.status,
|
|
127
|
+
.prepare(`SELECT s.id,s.name,s.status,s.template_id,t.head_version FROM schedules s
|
|
113
128
|
JOIN project_templates t ON t.id=s.template_id
|
|
114
|
-
|
|
115
|
-
WHERE s.deleted_at IS NULL AND s.status IN ('active','paused') AND ${mentions("r.document_json")}
|
|
129
|
+
WHERE s.deleted_at IS NULL AND s.status IN ('active','paused') AND s.template_kind='project'
|
|
116
130
|
ORDER BY lower(s.name),s.id`)
|
|
117
|
-
.all(
|
|
131
|
+
.all()
|
|
118
132
|
.flatMap((row) => {
|
|
119
133
|
const parsed = scheduleRow.parse(row);
|
|
120
|
-
return matches(
|
|
134
|
+
return matches(formRun(parsed.template_id, parsed.head_version))
|
|
121
135
|
? [{ id: parsed.id, name: parsed.name, status: parsed.status }]
|
|
122
136
|
: [];
|
|
123
137
|
});
|
|
@@ -126,7 +140,7 @@ export function usedBy(db, ref, formerNames = []) {
|
|
|
126
140
|
(SELECT count(*) FROM project_revisions x WHERE x.project_id=r.project_id) AS total
|
|
127
141
|
FROM project_revisions r JOIN projects p ON p.id=r.project_id
|
|
128
142
|
LEFT JOIN project_heads h ON h.project_id=r.project_id
|
|
129
|
-
WHERE ${liveProject("p")} AND ${mentions("r.config")}
|
|
143
|
+
WHERE ${liveProject("p")} AND ${notTemplateSample("p")} AND ${mentions("r.config")}
|
|
130
144
|
ORDER BY p.created_at DESC,p.id,r.created_at`)
|
|
131
145
|
.all(...plain)
|
|
132
146
|
.map((row) => revisionRow.parse(row));
|
|
@@ -146,7 +160,7 @@ export function usedBy(db, ref, formerNames = []) {
|
|
|
146
160
|
// A project from before revisions existed has none yet; its own config is what it ran with.
|
|
147
161
|
for (const row of db
|
|
148
162
|
.prepare(`SELECT p.id,p.title,p.config FROM projects p
|
|
149
|
-
WHERE ${liveProject("p")}
|
|
163
|
+
WHERE ${liveProject("p")} AND ${notTemplateSample("p")}
|
|
150
164
|
AND NOT EXISTS (SELECT 1 FROM project_revisions r WHERE r.project_id=p.id)
|
|
151
165
|
AND ${mentions("p.config")} ORDER BY p.created_at DESC,p.id`)
|
|
152
166
|
.all(...plain)
|
|
@@ -167,7 +181,8 @@ const scheduleRow = z.object({
|
|
|
167
181
|
id: z.string(),
|
|
168
182
|
name: z.string(),
|
|
169
183
|
status: z.string(),
|
|
170
|
-
|
|
184
|
+
template_id: z.string(),
|
|
185
|
+
head_version: z.number(),
|
|
171
186
|
});
|
|
172
187
|
const revisionRow = z.object({
|
|
173
188
|
project_id: z.string(),
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { renameSync } from "node:fs";
|
|
1
|
+
import { renameSync, rmSync } from "node:fs";
|
|
2
2
|
import { z } from "zod";
|
|
3
3
|
import { runFfmpeg } from "../video/ffmpeg.js";
|
|
4
4
|
// ceiling: below -70 LUFS (the filter's absolute gate) or under 0.4 s (one measuring block)
|
|
@@ -89,6 +89,7 @@ export async function measure(run, input, goal, chain = "") {
|
|
|
89
89
|
args: measureArgs(input, goal, chain),
|
|
90
90
|
signal: run.signal,
|
|
91
91
|
log: run.log,
|
|
92
|
+
priority: run.priority,
|
|
92
93
|
onProgress: () => { },
|
|
93
94
|
onStderr: (text) => {
|
|
94
95
|
stderr += text;
|
|
@@ -109,8 +110,15 @@ export async function normalizeFile(run, input, output, goal, format) {
|
|
|
109
110
|
// The channels are set before anything is measured: a stereo file mixed down to mono is
|
|
110
111
|
// louder per channel (the mix keeps its loudness, so each sample is about 3 dB higher), and
|
|
111
112
|
// the gain and the peak ceiling have to hold for what is written.
|
|
112
|
-
const
|
|
113
|
-
|
|
113
|
+
const measured = await measure(run, ["-i", input], goal, channelsOf(format));
|
|
114
|
+
await writeLevelled(run, input, output, goal, measured, format);
|
|
115
|
+
return measured;
|
|
116
|
+
}
|
|
117
|
+
function channelsOf(format) {
|
|
118
|
+
return `aformat=channel_layouts=${format.channels === 1 ? "mono" : "stereo"}`;
|
|
119
|
+
}
|
|
120
|
+
// `input` written to `output` with the gain that takes `measured` to `goal` (`gainFilter`).
|
|
121
|
+
async function writeLevelled(run, input, output, goal, measured, format) {
|
|
114
122
|
await runFfmpeg({
|
|
115
123
|
bin: run.bin,
|
|
116
124
|
args: [
|
|
@@ -125,7 +133,7 @@ export async function normalizeFile(run, input, output, goal, format) {
|
|
|
125
133
|
"-map",
|
|
126
134
|
"0:a:0",
|
|
127
135
|
"-af",
|
|
128
|
-
`${
|
|
136
|
+
`${channelsOf(format)},${gainFilter(goal, measured, format.sampleRate)}`,
|
|
129
137
|
"-c:a",
|
|
130
138
|
"pcm_f32le",
|
|
131
139
|
"-f",
|
|
@@ -134,23 +142,55 @@ export async function normalizeFile(run, input, output, goal, format) {
|
|
|
134
142
|
],
|
|
135
143
|
signal: run.signal,
|
|
136
144
|
log: run.log,
|
|
145
|
+
priority: run.priority,
|
|
137
146
|
onProgress: () => { },
|
|
138
147
|
});
|
|
139
|
-
return measured;
|
|
140
148
|
}
|
|
141
149
|
// ceiling: within a fifth of a LU of the level no one hears the difference.
|
|
142
150
|
const correctAbove = 0.2;
|
|
143
|
-
//
|
|
144
|
-
//
|
|
145
|
-
|
|
151
|
+
// ceiling: two corrections land a voice whose peaks stand 17 dB over its loudness (raw TTS)
|
|
152
|
+
// within a few tenths of a LU; a third would only chase the last tenth.
|
|
153
|
+
const corrections = 2;
|
|
154
|
+
// ceiling: the most gain a correction adds over the plain gain. Past it the limiter would
|
|
155
|
+
// flatten the voice; the file lands short instead, and its report says how far.
|
|
156
|
+
const extraGainMax = 6;
|
|
157
|
+
// One file brought to `goal` (`normalizeFile`), measured again, and corrected when the limiter
|
|
158
|
+
// left it more than a fifth of a LU under the level. Each correction writes the input again
|
|
159
|
+
// with more gain (so the limiter holds the peaks once, never twice), the gain found from the
|
|
160
|
+
// levels the earlier gains gave. Returns the first measurement and the last.
|
|
146
161
|
export async function levelFile(run, input, output, goal, format) {
|
|
147
162
|
const before = await normalizeFile(run, input, output, goal, format);
|
|
148
163
|
let after = await measureFile(run, output, goal);
|
|
149
|
-
if (measurable(
|
|
164
|
+
if (!measurable(before))
|
|
165
|
+
return { before, after };
|
|
166
|
+
const plain = goal.lufs - before.integrated;
|
|
167
|
+
const tried = [
|
|
168
|
+
{ gain: plain, lufs: after.integrated },
|
|
169
|
+
];
|
|
170
|
+
for (let pass = 0; pass < corrections; pass += 1) {
|
|
171
|
+
if (!measurable(after) || Math.abs(after.integrated - goal.lufs) <= correctAbove)
|
|
172
|
+
break;
|
|
173
|
+
const last = tried[tried.length - 1];
|
|
174
|
+
const prior = tried[tried.length - 2];
|
|
175
|
+
if (last === undefined)
|
|
176
|
+
break;
|
|
177
|
+
const slope = prior === undefined || last.lufs === prior.lufs
|
|
178
|
+
? 1
|
|
179
|
+
: (last.lufs - prior.lufs) / (last.gain - prior.gain);
|
|
180
|
+
const gain = Math.min(plain + extraGainMax, last.gain + (goal.lufs - last.lufs) / (slope > 0.05 ? slope : 1));
|
|
181
|
+
if (Math.abs(gain - last.gain) < 0.05)
|
|
182
|
+
break;
|
|
150
183
|
const corrected = `${output}.corrected.wav`;
|
|
151
|
-
await
|
|
184
|
+
await writeLevelled(run, input, corrected, { ...goal, lufs: before.integrated + gain }, before, format);
|
|
185
|
+
const next = await measureFile(run, corrected, goal);
|
|
186
|
+
// A correction that landed further off than the file it would replace is dropped.
|
|
187
|
+
if (Math.abs(next.integrated - goal.lufs) >= Math.abs(after.integrated - goal.lufs)) {
|
|
188
|
+
rmSync(corrected, { force: true });
|
|
189
|
+
break;
|
|
190
|
+
}
|
|
152
191
|
renameSync(corrected, output);
|
|
153
|
-
after =
|
|
192
|
+
after = next;
|
|
193
|
+
tried.push({ gain, lufs: next.integrated });
|
|
154
194
|
}
|
|
155
195
|
return { before, after };
|
|
156
196
|
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { isVideoModel } from "../../catalog/schema.js";
|
|
2
|
+
import { priceCall } from "../run-cost/pricing.js";
|
|
3
|
+
import { isLocalCliProvider, systemVoiceProvider } from "../settings/model.js";
|
|
4
|
+
// ceiling: an automatic check may cost at most this much per call (decision 1). Anything
|
|
5
|
+
// dearer - and every image - waits for Check models and a confirmed estimate.
|
|
6
|
+
export const automaticCeilingDollars = 0.001;
|
|
7
|
+
// What a check is assumed to use before it runs: a short prompt and a one-word reply, two
|
|
8
|
+
// characters of speech, one small image.
|
|
9
|
+
const assumed = { tokensIn: 40, tokensOut: 40, characters: 2, images: 1 };
|
|
10
|
+
export function isVideoTarget(catalogue, target) {
|
|
11
|
+
return (target.family === "image" &&
|
|
12
|
+
catalogue.image.some((row) => row.provider === target.provider && row.id === target.model && isVideoModel(row)));
|
|
13
|
+
}
|
|
14
|
+
export function costOf(catalogue, target) {
|
|
15
|
+
if (target.provider === systemVoiceProvider)
|
|
16
|
+
return { dollars: 0, planUsage: false };
|
|
17
|
+
if (isLocalCliProvider(target.provider))
|
|
18
|
+
return { dollars: 0, planUsage: true };
|
|
19
|
+
return { dollars: priced(catalogue, target, assumed), planUsage: false };
|
|
20
|
+
}
|
|
21
|
+
// What a finished check cost, from what it reported; the estimate when it reported nothing.
|
|
22
|
+
export function spentOn(catalogue, target, use) {
|
|
23
|
+
if (isLocalCliProvider(target.provider) || target.provider === systemVoiceProvider)
|
|
24
|
+
return 0;
|
|
25
|
+
const reported = use === null
|
|
26
|
+
? null
|
|
27
|
+
: priced(catalogue, target, {
|
|
28
|
+
tokensIn: use.tokensIn ?? assumed.tokensIn,
|
|
29
|
+
tokensOut: use.tokensOut ?? assumed.tokensOut,
|
|
30
|
+
characters: use.characters ?? assumed.characters,
|
|
31
|
+
images: use.images ?? assumed.images,
|
|
32
|
+
});
|
|
33
|
+
return reported ?? costOf(catalogue, target).dollars ?? 0;
|
|
34
|
+
}
|
|
35
|
+
function priced(catalogue, target, use) {
|
|
36
|
+
const call = {
|
|
37
|
+
kind: target.family,
|
|
38
|
+
provider: target.provider,
|
|
39
|
+
model: target.model,
|
|
40
|
+
wallMs: 0,
|
|
41
|
+
...use,
|
|
42
|
+
};
|
|
43
|
+
const price = priceCall(call, catalogue);
|
|
44
|
+
if (price.cost !== null)
|
|
45
|
+
return price.cost;
|
|
46
|
+
// A voice priced by the minute: two characters are well under a second.
|
|
47
|
+
const perMinute = target.family === "tts"
|
|
48
|
+
? catalogue.tts.find((row) => row.provider === target.provider && row.id === target.model)
|
|
49
|
+
?.pricing.perMinute
|
|
50
|
+
: undefined;
|
|
51
|
+
return perMinute === undefined ? null : perMinute / 60;
|
|
52
|
+
}
|
|
53
|
+
// Automatic checks (a key or CLI added, the weekly look) run only what costs nothing, a CLI
|
|
54
|
+
// plan's allowance, or a text or voice call under the ceiling. Images and clips never.
|
|
55
|
+
export function automaticAllowed(catalogue, target, cost) {
|
|
56
|
+
if (target.family === "image" || isVideoTarget(catalogue, target))
|
|
57
|
+
return false;
|
|
58
|
+
if (cost.planUsage)
|
|
59
|
+
return true;
|
|
60
|
+
return cost.dollars !== null && cost.dollars < automaticCeilingDollars;
|
|
61
|
+
}
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import { keyWithShared } from "../settings/keys.js";
|
|
2
|
+
import { providerById } from "../settings/model.js";
|
|
3
|
+
import { probeRead } from "../settings/probe-read.js";
|
|
4
|
+
import { accountOf, writeListed } from "./repo.js";
|
|
5
|
+
// ceiling: a list longer than this many pages is read no further.
|
|
6
|
+
const maxPages = 10;
|
|
7
|
+
async function liveList(deps, probe, key) {
|
|
8
|
+
const list = probe.list;
|
|
9
|
+
if (list === undefined)
|
|
10
|
+
return undefined;
|
|
11
|
+
const ids = new Set();
|
|
12
|
+
let page;
|
|
13
|
+
for (let n = 0; n < maxPages; n += 1) {
|
|
14
|
+
const answer = await probeRead(deps.fetch, list.url(page), probe.headers(key), true);
|
|
15
|
+
if (answer === undefined || answer.status < 200 || answer.status >= 300)
|
|
16
|
+
return undefined;
|
|
17
|
+
const read = list.read(answer.body);
|
|
18
|
+
if (read === undefined)
|
|
19
|
+
return undefined;
|
|
20
|
+
for (const id of read.ids)
|
|
21
|
+
ids.add(id);
|
|
22
|
+
if (read.next === undefined)
|
|
23
|
+
break;
|
|
24
|
+
page = read.next;
|
|
25
|
+
}
|
|
26
|
+
return ids;
|
|
27
|
+
}
|
|
28
|
+
// One read per model's own page: found, missing, or (no answer) left as it was.
|
|
29
|
+
async function modelPages(deps, probe, key, models) {
|
|
30
|
+
const found = new Map();
|
|
31
|
+
const model = probe.model;
|
|
32
|
+
if (model === undefined)
|
|
33
|
+
return found;
|
|
34
|
+
for (const id of models) {
|
|
35
|
+
const answer = await probeRead(deps.fetch, model.url(id), probe.headers(key), false);
|
|
36
|
+
if (answer === undefined)
|
|
37
|
+
continue;
|
|
38
|
+
if (answer.status >= 200 && answer.status < 300)
|
|
39
|
+
found.set(id, true);
|
|
40
|
+
else if (answer.status === 404)
|
|
41
|
+
found.set(id, false);
|
|
42
|
+
}
|
|
43
|
+
return found;
|
|
44
|
+
}
|
|
45
|
+
export async function listedFor(deps, provider) {
|
|
46
|
+
const { family, auth } = providerById(provider);
|
|
47
|
+
if (auth === "local")
|
|
48
|
+
return { kind: "skipped", reason: "no-list" };
|
|
49
|
+
const account = accountOf(deps.db, provider);
|
|
50
|
+
if (account === undefined)
|
|
51
|
+
return { kind: "skipped", reason: "no-key" };
|
|
52
|
+
let models;
|
|
53
|
+
if (auth === "cli") {
|
|
54
|
+
let ids;
|
|
55
|
+
try {
|
|
56
|
+
ids = await deps.cliModels(provider, family);
|
|
57
|
+
}
|
|
58
|
+
catch {
|
|
59
|
+
return { kind: "skipped", reason: "unreadable" };
|
|
60
|
+
}
|
|
61
|
+
models = new Map(ids.map((id) => [id, true]));
|
|
62
|
+
}
|
|
63
|
+
else {
|
|
64
|
+
const probe = Object.hasOwn(deps.probes, provider) ? deps.probes[provider] : undefined;
|
|
65
|
+
const key = keyWithShared(deps.db, provider);
|
|
66
|
+
if (key === undefined)
|
|
67
|
+
return { kind: "skipped", reason: "no-key" };
|
|
68
|
+
const known = deps.known(provider, family);
|
|
69
|
+
if (probe?.list !== undefined) {
|
|
70
|
+
const live = await liveList(deps, probe, key);
|
|
71
|
+
if (live === undefined)
|
|
72
|
+
return { kind: "skipped", reason: "unreadable" };
|
|
73
|
+
models = new Map(known.map((id) => [id, live.has(id)]));
|
|
74
|
+
}
|
|
75
|
+
else if (probe?.model !== undefined && probe.model.lists === undefined) {
|
|
76
|
+
models = await modelPages(deps, probe, key, known);
|
|
77
|
+
}
|
|
78
|
+
else
|
|
79
|
+
return { kind: "skipped", reason: "no-list" };
|
|
80
|
+
}
|
|
81
|
+
writeListed(deps.db, {
|
|
82
|
+
provider,
|
|
83
|
+
family,
|
|
84
|
+
account,
|
|
85
|
+
at: deps.clock.now().toISOString(),
|
|
86
|
+
models,
|
|
87
|
+
});
|
|
88
|
+
return {
|
|
89
|
+
kind: "listed",
|
|
90
|
+
listed: [...models.values()].filter(Boolean).length,
|
|
91
|
+
of: models.size,
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
// ceiling: a provider's list is read at most this often for the same key or login, however
|
|
95
|
+
// many pickers open; a key saved, a CLI added or Check models forces a new read.
|
|
96
|
+
export const listingFreshMs = 6 * 60 * 60_000;
|
|
97
|
+
export function createListing(deps) {
|
|
98
|
+
const last = new Map();
|
|
99
|
+
const running = new Map();
|
|
100
|
+
return {
|
|
101
|
+
refresh: (provider, force = false) => {
|
|
102
|
+
const inFlight = running.get(provider);
|
|
103
|
+
if (inFlight !== undefined)
|
|
104
|
+
return inFlight;
|
|
105
|
+
const account = accountOf(deps.db, provider);
|
|
106
|
+
const seen = last.get(provider);
|
|
107
|
+
const now = deps.clock.now().getTime();
|
|
108
|
+
if (!force &&
|
|
109
|
+
seen !== undefined &&
|
|
110
|
+
seen.account === account &&
|
|
111
|
+
now - seen.at < listingFreshMs)
|
|
112
|
+
return Promise.resolve(undefined);
|
|
113
|
+
const started = listedFor(deps, provider)
|
|
114
|
+
.then((outcome) => {
|
|
115
|
+
if (account !== undefined)
|
|
116
|
+
last.set(provider, { account, at: now });
|
|
117
|
+
return outcome;
|
|
118
|
+
})
|
|
119
|
+
.finally(() => running.delete(provider));
|
|
120
|
+
running.set(provider, started);
|
|
121
|
+
return started;
|
|
122
|
+
},
|
|
123
|
+
};
|
|
124
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
// What Slopify knows about one model for the person's own key or CLI login. Only the pickers
|
|
2
|
+
// read it: a run never asks, so no standing ever changes what a run does.
|
|
3
|
+
// works: a check call or a real run succeeded. listed: the key's or CLI's live list names it.
|
|
4
|
+
// known: only Slopify's model list (models.yaml) names it. no-access: the provider said the
|
|
5
|
+
// account can't use it.
|
|
6
|
+
export const modelStandings = ["works", "listed", "known", "no-access"];
|
|
7
|
+
export const checkTriggers = ["manual", "key-added", "cli-added", "weekly", "run"];
|
|
8
|
+
// failed: the check could not tell (rate limit, network, the provider down); it keeps
|
|
9
|
+
// whatever was known before.
|
|
10
|
+
export const checkVerdicts = ["works", "no-access", "failed"];
|
|
11
|
+
export const verdictSources = ["check", "run"];
|
|
12
|
+
// Settings → Models → Automatic checks. free: only checks that cost nothing or well under a
|
|
13
|
+
// tenth of a cent each; off: only Check models.
|
|
14
|
+
export const autoModes = ["free", "off"];
|
|
15
|
+
export const weeklyAtKey = "model-checks.weekly-at";
|
|
16
|
+
export const autoKey = "model-checks.auto";
|
|
17
|
+
export const baselineKey = "model-checks.baseline-done";
|
|
18
|
+
export function keyOfModel(key) {
|
|
19
|
+
return `${key.family}:${key.provider}:${key.model}`;
|
|
20
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { providerIds } from "../settings/model.js";
|
|
2
|
+
import { accountOf, writeVerdict } from "./repo.js";
|
|
3
|
+
function known(provider) {
|
|
4
|
+
return providerIds.includes(provider);
|
|
5
|
+
}
|
|
6
|
+
// Learn from real runs: a model a run used works, and one the provider refused as not on the
|
|
7
|
+
// plan is marked so, with the provider's sentence, the moment it happens. A call made without
|
|
8
|
+
// a key on file (none to tie the row to) teaches nothing.
|
|
9
|
+
export function accessRecorder(deps) {
|
|
10
|
+
const write = (use, verdict, sentence) => {
|
|
11
|
+
if (!known(use.provider))
|
|
12
|
+
return;
|
|
13
|
+
const account = accountOf(deps.db, use.provider);
|
|
14
|
+
if (account === undefined)
|
|
15
|
+
return;
|
|
16
|
+
writeVerdict(deps.db, {
|
|
17
|
+
provider: use.provider,
|
|
18
|
+
family: use.family,
|
|
19
|
+
model: use.model,
|
|
20
|
+
account,
|
|
21
|
+
verdict,
|
|
22
|
+
source: "run",
|
|
23
|
+
sentence,
|
|
24
|
+
at: deps.clock.now().toISOString(),
|
|
25
|
+
cost: 0,
|
|
26
|
+
});
|
|
27
|
+
};
|
|
28
|
+
return {
|
|
29
|
+
worked: (use) => write(use, "works", null),
|
|
30
|
+
denied: (use) => write(use, "no-access", use.sentence),
|
|
31
|
+
};
|
|
32
|
+
}
|