@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
|
@@ -33,6 +33,26 @@ export function getRevisionView(deps, projectId, revisionId) {
|
|
|
33
33
|
articleMarkdown: resolvedArticleMarkdown(deps, revision, outputs),
|
|
34
34
|
};
|
|
35
35
|
}
|
|
36
|
+
export function isReadyOutput(row, match = {}) {
|
|
37
|
+
return (row.selected &&
|
|
38
|
+
row.available &&
|
|
39
|
+
row.state === "ready" &&
|
|
40
|
+
(match.role === undefined || row.output.role === match.role) &&
|
|
41
|
+
(match.workKey === undefined || row.workKey === match.workKey));
|
|
42
|
+
}
|
|
43
|
+
export function readyOutput(view, match = {}) {
|
|
44
|
+
return view.outputs.find((row) => isReadyOutput(row, match));
|
|
45
|
+
}
|
|
46
|
+
// A piece a step can read: selected, its file (if any) on disk and done, under `key` when given.
|
|
47
|
+
export function isDonePiece(row, key) {
|
|
48
|
+
return (row.selected &&
|
|
49
|
+
row.available &&
|
|
50
|
+
row.piece.state === "done" &&
|
|
51
|
+
(key === undefined || row.key === key));
|
|
52
|
+
}
|
|
53
|
+
export function donePiece(view, key) {
|
|
54
|
+
return view.pieces.find((row) => isDonePiece(row, key));
|
|
55
|
+
}
|
|
36
56
|
export function resolvedArticleMarkdown(deps, revision, outputs) {
|
|
37
57
|
if (revision.content.articleEdited === true && revision.content.articleMarkdown !== undefined)
|
|
38
58
|
return revision.content.articleMarkdown;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { causedBy } from "../../kernel/errors.js";
|
|
3
|
+
import { notTemplateSample } from "../template-studio/repo.js";
|
|
3
4
|
// Living within a CLI's plan limits. When a CLI says its allowance is used up, the time it
|
|
4
5
|
// returns is stored per account; every call to that CLI - from any project, and after a
|
|
5
6
|
// restart - waits for it instead of failing. Other providers are untouched, so a project
|
|
@@ -99,13 +100,15 @@ export function createLimitGate(deps) {
|
|
|
99
100
|
// At start, a stage that was waiting on a plan limit when the app stopped was marked
|
|
100
101
|
// interrupted like any other (main.ts markInterruptedStages). Those projects are resumed so
|
|
101
102
|
// the wait carries on by itself - their calls meet the stored reset and wait again. A project
|
|
102
|
-
// paused or cancelled meanwhile is left as it is.
|
|
103
|
+
// paused or cancelled meanwhile is left as it is. A template studio sample is the studio's own
|
|
104
|
+
// recovery's to resume, which checks the sample budget and its plans switch first.
|
|
103
105
|
export async function resumeAfterRestart(db, resume, log) {
|
|
104
106
|
const projects = db
|
|
105
107
|
.prepare(`SELECT DISTINCT w.project_id AS id FROM plan_limit_waiters w
|
|
106
108
|
JOIN stages s ON s.project_id = w.project_id AND s.kind = w.stage
|
|
107
109
|
WHERE s.state = 'failed' AND s.failure_reason = 'interrupted'
|
|
108
|
-
AND NOT EXISTS (SELECT 1 FROM project_controls c WHERE c.project_id = w.project_id AND c.paused = 1)
|
|
110
|
+
AND NOT EXISTS (SELECT 1 FROM project_controls c WHERE c.project_id = w.project_id AND c.paused = 1)
|
|
111
|
+
AND ${notTemplateSample("w", "project_id")}`)
|
|
109
112
|
.all()
|
|
110
113
|
.map((row) => z.string().parse(row.id));
|
|
111
114
|
db.prepare("DELETE FROM plan_limit_waiters").run();
|
|
@@ -9,15 +9,17 @@ export function createUsageMeter(deps) {
|
|
|
9
9
|
record: (call) => {
|
|
10
10
|
const priced = priceCall(call, deps.catalogue());
|
|
11
11
|
const at = deps.clock.now().toISOString();
|
|
12
|
+
const usageId = deps.ids.next();
|
|
12
13
|
deps.db
|
|
13
14
|
.prepare(`INSERT INTO provider_usage (id, project_id, stage, kind, provider, model, tokens_in, tokens_out, tokens_cached, characters, images, seconds, size, quality, wall_ms, on_plan, cost, api_model, api_cost, price_json, created_at)
|
|
14
15
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`)
|
|
15
|
-
.run(
|
|
16
|
+
.run(usageId, call.projectId, call.stage, call.kind, call.provider, call.model, call.tokensIn ?? null, call.tokensOut ?? null, call.cachedTokens ?? null, call.characters ?? null, call.images ?? null, call.seconds ?? null, call.size ?? null, call.quality ?? null, Math.round(call.wallMs), priced.onPlan ? 1 : 0, priced.cost, priced.apiModel, priced.apiCost, priced.price === null ? null : JSON.stringify(priced.price), at);
|
|
17
|
+
// The reading names its call, so the Run cost tab knows when the call ran and its tokens.
|
|
16
18
|
const account = planAccountOf(call.provider);
|
|
17
19
|
if (account !== undefined && call.limits !== undefined)
|
|
18
20
|
deps.db
|
|
19
21
|
.prepare("INSERT INTO plan_limit_readings (id, project_id, account, reading_json, created_at) VALUES (?, ?, ?, ?, ?)")
|
|
20
|
-
.run(deps.ids.next(), call.projectId, account, JSON.stringify(call.limits), at);
|
|
22
|
+
.run(deps.ids.next(), call.projectId, account, JSON.stringify({ ...call.limits, usageId }), at);
|
|
21
23
|
},
|
|
22
24
|
};
|
|
23
25
|
}
|
|
@@ -40,8 +42,13 @@ export function createStandaloneMeter(deps) {
|
|
|
40
42
|
}
|
|
41
43
|
// A schedule counts under its template's channel; one whose template is gone, like a channel
|
|
42
44
|
// this install doesn't have, under the default channel.
|
|
45
|
+
// A design counts under its own channel; one deleted since, under the default channel.
|
|
43
46
|
function channelOf(db, owner) {
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
+
if (owner.kind === "schedule")
|
|
48
|
+
return resolveChannelId(db, scheduleChannel(db, owner.id)?.id);
|
|
49
|
+
if (owner.kind === "design") {
|
|
50
|
+
const row = db.prepare("SELECT channel_id FROM designs WHERE id=?").get(owner.id);
|
|
51
|
+
return resolveChannelId(db, row === undefined ? undefined : String(row.channel_id));
|
|
52
|
+
}
|
|
53
|
+
return resolveChannelId(db, owner.id);
|
|
47
54
|
}
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { stageKinds } from "../../kernel/pipeline.js";
|
|
3
|
-
import { planAccountNames,
|
|
3
|
+
import { planAccountNames, planAccounts, } from "../../kernel/ports/plan-limits.js";
|
|
4
|
+
import { isSampleStandIn } from "../settings/model.js";
|
|
5
|
+
import { planUses } from "./plan-readings.js";
|
|
4
6
|
import { projectTiming } from "./timing.js";
|
|
5
7
|
const usageRow = z.object({
|
|
6
8
|
stage: z.enum(stageKinds),
|
|
@@ -19,15 +21,6 @@ const usageRow = z.object({
|
|
|
19
21
|
api_cost: z.number().nullable(),
|
|
20
22
|
price_json: z.string().nullable(),
|
|
21
23
|
});
|
|
22
|
-
const windowSchema = z.object({
|
|
23
|
-
kind: z.enum(["five_hour", "weekly", "other"]),
|
|
24
|
-
usedPercent: z.number(),
|
|
25
|
-
resetsAt: z.string().nullable(),
|
|
26
|
-
});
|
|
27
|
-
const readingSchema = z.object({
|
|
28
|
-
before: z.array(windowSchema).optional(),
|
|
29
|
-
after: z.array(windowSchema).optional(),
|
|
30
|
-
});
|
|
31
24
|
const emptyTotals = {
|
|
32
25
|
tokensIn: 0,
|
|
33
26
|
tokensOut: 0,
|
|
@@ -45,11 +38,13 @@ const emptyLine = {
|
|
|
45
38
|
};
|
|
46
39
|
function add(into, row) {
|
|
47
40
|
const onPlan = row.on_plan === 1;
|
|
41
|
+
// The bundled samples' calls were stored with no price; their stand-ins are free.
|
|
42
|
+
const cost = row.cost ?? (isSampleStandIn(row.provider) ? 0 : null);
|
|
48
43
|
return {
|
|
49
44
|
...into,
|
|
50
45
|
calls: into.calls + 1,
|
|
51
|
-
cost: into.cost + (
|
|
52
|
-
unpriced: into.unpriced + (
|
|
46
|
+
cost: into.cost + (cost ?? 0),
|
|
47
|
+
unpriced: into.unpriced + (cost === null ? 1 : 0),
|
|
53
48
|
apiEquivalent: onPlan ? (into.apiEquivalent ?? 0) + (row.api_cost ?? 0) : into.apiEquivalent,
|
|
54
49
|
apiUnpriced: into.apiUnpriced + (onPlan && row.api_cost === null ? 1 : 0),
|
|
55
50
|
tokensIn: into.tokensIn + (row.tokens_in ?? 0),
|
|
@@ -95,7 +90,7 @@ export function runCostOf(db, projectId, now = Date.now()) {
|
|
|
95
90
|
for (const [stage, wallMs] of walls)
|
|
96
91
|
if (!stages.has(stage))
|
|
97
92
|
stages.set(stage, { stage, wallMs, ...emptyLine, ...emptyTotals });
|
|
98
|
-
const plans = planUses(db, projectId, rows);
|
|
93
|
+
const plans = planUses(db, projectId, rows.map((row) => row.provider));
|
|
99
94
|
return {
|
|
100
95
|
currency: "USD",
|
|
101
96
|
...total,
|
|
@@ -128,67 +123,6 @@ function catalogueOf(json) {
|
|
|
128
123
|
const parsed = z.object({ catalogue: z.string() }).safeParse(JSON.parse(json));
|
|
129
124
|
return parsed.success ? parsed.data.catalogue : null;
|
|
130
125
|
}
|
|
131
|
-
// Two readings belong to the same window when their resets agree to within a few minutes;
|
|
132
|
-
// a reset moving on means the window rolled over between them.
|
|
133
|
-
const sameWindowMs = 5 * 60_000;
|
|
134
|
-
export function windowUse(readings) {
|
|
135
|
-
const byKind = new Map();
|
|
136
|
-
for (const reading of readings)
|
|
137
|
-
for (const window of reading)
|
|
138
|
-
byKind.set(window.kind, [...(byKind.get(window.kind) ?? []), window]);
|
|
139
|
-
const uses = [];
|
|
140
|
-
for (const [kind, seen] of byKind) {
|
|
141
|
-
let used = 0;
|
|
142
|
-
for (let at = 1; at < seen.length; at += 1) {
|
|
143
|
-
const previous = seen[at - 1];
|
|
144
|
-
const current = seen[at];
|
|
145
|
-
if (previous === undefined || current === undefined)
|
|
146
|
-
continue;
|
|
147
|
-
const same = previous.resetsAt === current.resetsAt ||
|
|
148
|
-
(previous.resetsAt !== null &&
|
|
149
|
-
current.resetsAt !== null &&
|
|
150
|
-
Math.abs(Date.parse(previous.resetsAt) - Date.parse(current.resetsAt)) < sameWindowMs);
|
|
151
|
-
used += same ? Math.max(0, current.usedPercent - previous.usedPercent) : current.usedPercent;
|
|
152
|
-
}
|
|
153
|
-
const last = seen.at(-1);
|
|
154
|
-
if (last !== undefined)
|
|
155
|
-
uses.push({ kind, usedPercent: used, nowPercent: last.usedPercent, resetsAt: last.resetsAt });
|
|
156
|
-
}
|
|
157
|
-
const order = ["five_hour", "weekly", "other"];
|
|
158
|
-
return uses.toSorted((left, right) => order.indexOf(left.kind) - order.indexOf(right.kind));
|
|
159
|
-
}
|
|
160
|
-
function planUses(db, projectId, rows) {
|
|
161
|
-
const readings = db
|
|
162
|
-
.prepare("SELECT account, reading_json FROM plan_limit_readings WHERE project_id = ? ORDER BY created_at, rowid")
|
|
163
|
-
.all(projectId)
|
|
164
|
-
.map((row) => z.object({ account: z.string(), reading_json: z.string() }).parse(row));
|
|
165
|
-
return planAccounts.flatMap((account) => {
|
|
166
|
-
const calls = rows.filter((row) => planAccountOf(row.provider) === account).length;
|
|
167
|
-
if (calls === 0)
|
|
168
|
-
return [];
|
|
169
|
-
const sequence = [];
|
|
170
|
-
for (const row of readings) {
|
|
171
|
-
if (row.account !== account)
|
|
172
|
-
continue;
|
|
173
|
-
const parsed = readingSchema.safeParse(JSON.parse(row.reading_json));
|
|
174
|
-
if (!parsed.success)
|
|
175
|
-
continue;
|
|
176
|
-
if (parsed.data.before?.length)
|
|
177
|
-
sequence.push(parsed.data.before);
|
|
178
|
-
if (parsed.data.after?.length)
|
|
179
|
-
sequence.push(parsed.data.after);
|
|
180
|
-
}
|
|
181
|
-
return [
|
|
182
|
-
{
|
|
183
|
-
account,
|
|
184
|
-
name: planAccountNames[account],
|
|
185
|
-
calls,
|
|
186
|
-
reported: sequence.length > 0,
|
|
187
|
-
windows: windowUse(sequence),
|
|
188
|
-
},
|
|
189
|
-
];
|
|
190
|
-
});
|
|
191
|
-
}
|
|
192
126
|
export function limitWaitsOf(db, projectId) {
|
|
193
127
|
return limitWaitRows(db, projectId).map((row) => row.wait);
|
|
194
128
|
}
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { planAccountNames, planAccountOf, planAccounts, } from "../../kernel/ports/plan-limits.js";
|
|
3
|
+
import { beforeGapMs, windowUse, } from "./plan-use.js";
|
|
4
|
+
const windowSchema = z.object({
|
|
5
|
+
kind: z.enum(["five_hour", "weekly", "other"]),
|
|
6
|
+
usedPercent: z.number(),
|
|
7
|
+
resetsAt: z.string().nullable(),
|
|
8
|
+
});
|
|
9
|
+
// `usageId` names the call's provider_usage row; readings stored before it was kept lack it.
|
|
10
|
+
export const readingSchema = z.object({
|
|
11
|
+
before: z.array(windowSchema).optional(),
|
|
12
|
+
after: z.array(windowSchema).optional(),
|
|
13
|
+
usageId: z.string().optional(),
|
|
14
|
+
});
|
|
15
|
+
const callRow = z.object({
|
|
16
|
+
id: z.string(),
|
|
17
|
+
provider: z.string(),
|
|
18
|
+
tokens_in: z.number().nullable(),
|
|
19
|
+
tokens_out: z.number().nullable(),
|
|
20
|
+
wall_ms: z.number(),
|
|
21
|
+
created_at: z.string(),
|
|
22
|
+
});
|
|
23
|
+
const readingRow = z.object({
|
|
24
|
+
id: z.string(),
|
|
25
|
+
account: z.string().nullable(),
|
|
26
|
+
reading_json: z.string().nullable(),
|
|
27
|
+
created_at: z.string(),
|
|
28
|
+
});
|
|
29
|
+
function callOf(row) {
|
|
30
|
+
const endMs = Date.parse(row.created_at);
|
|
31
|
+
return {
|
|
32
|
+
id: row.id,
|
|
33
|
+
startMs: endMs - row.wall_ms,
|
|
34
|
+
endMs,
|
|
35
|
+
tokens: row.tokens_in === null && row.tokens_out === null
|
|
36
|
+
? null
|
|
37
|
+
: (row.tokens_in ?? 0) + (row.tokens_out ?? 0),
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
function parseReading(json) {
|
|
41
|
+
if (json === null)
|
|
42
|
+
return undefined;
|
|
43
|
+
const parsed = readingSchema.safeParse(JSON.parse(json));
|
|
44
|
+
return parsed.success ? parsed.data : undefined;
|
|
45
|
+
}
|
|
46
|
+
function readingOf(stored, usageId, createdAt, calls) {
|
|
47
|
+
const call = usageId === undefined ? undefined : calls.get(usageId);
|
|
48
|
+
const at = Date.parse(createdAt);
|
|
49
|
+
return {
|
|
50
|
+
usageId: call === undefined ? null : call.id,
|
|
51
|
+
startMs: call?.startMs ?? at,
|
|
52
|
+
endMs: call?.endMs ?? at,
|
|
53
|
+
tokens: call?.tokens ?? null,
|
|
54
|
+
before: stored.before,
|
|
55
|
+
after: stored.after,
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
export function planUses(db, projectId, providers) {
|
|
59
|
+
const ownCalls = db
|
|
60
|
+
.prepare("SELECT id, provider, tokens_in, tokens_out, wall_ms, created_at FROM provider_usage WHERE project_id = ?")
|
|
61
|
+
.all(projectId)
|
|
62
|
+
.map((row) => callRow.parse(row));
|
|
63
|
+
const ownReadings = db
|
|
64
|
+
.prepare("SELECT id, account, reading_json, created_at FROM plan_limit_readings WHERE project_id = ? ORDER BY created_at, rowid")
|
|
65
|
+
.all(projectId)
|
|
66
|
+
.map((row) => readingRow.parse(row));
|
|
67
|
+
return planAccounts.flatMap((account) => {
|
|
68
|
+
const calls = providers.filter((provider) => planAccountOf(provider) === account).length;
|
|
69
|
+
if (calls === 0)
|
|
70
|
+
return [];
|
|
71
|
+
const mine = ownReadings.filter((row) => row.account === account);
|
|
72
|
+
const activity = accountActivity(db, account, ownCalls, projectId);
|
|
73
|
+
const own = mine.flatMap((row) => {
|
|
74
|
+
const stored = parseReading(row.reading_json);
|
|
75
|
+
if (stored === undefined || (!stored.before?.length && !stored.after?.length))
|
|
76
|
+
return [];
|
|
77
|
+
return [readingOf(stored, stored.usageId, row.created_at, activity.byId)];
|
|
78
|
+
});
|
|
79
|
+
return [
|
|
80
|
+
{
|
|
81
|
+
account,
|
|
82
|
+
name: planAccountNames[account],
|
|
83
|
+
calls,
|
|
84
|
+
reported: own.length > 0,
|
|
85
|
+
windows: windowUse(own, {
|
|
86
|
+
calls: [...activity.byId.values()],
|
|
87
|
+
readings: activity.readings,
|
|
88
|
+
}),
|
|
89
|
+
},
|
|
90
|
+
];
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
// Every call on the account from when this project's first call began (less the gap an
|
|
94
|
+
// after-only call may look back), from any project or schedule.
|
|
95
|
+
function accountActivity(db, account, ownCalls, projectId) {
|
|
96
|
+
const starts = ownCalls.map((row) => callOf(row).startMs);
|
|
97
|
+
const byId = new Map();
|
|
98
|
+
if (starts.length === 0)
|
|
99
|
+
return { byId, readings: [] };
|
|
100
|
+
const since = new Date(Math.min(...starts) - beforeGapMs).toISOString();
|
|
101
|
+
const projectCalls = db
|
|
102
|
+
.prepare("SELECT id, provider, tokens_in, tokens_out, wall_ms, created_at FROM provider_usage WHERE created_at >= ?")
|
|
103
|
+
.all(since)
|
|
104
|
+
.map((row) => callRow.parse(row));
|
|
105
|
+
const standalone = db
|
|
106
|
+
.prepare("SELECT id, provider, tokens_in, tokens_out, wall_ms, created_at, account, reading_json FROM standalone_usage WHERE created_at >= ?")
|
|
107
|
+
.all(since);
|
|
108
|
+
for (const row of [...projectCalls, ...standalone.map((raw) => callRow.parse(raw))])
|
|
109
|
+
if (planAccountOf(row.provider) === account)
|
|
110
|
+
byId.set(row.id, callOf(row));
|
|
111
|
+
const readings = [];
|
|
112
|
+
for (const raw of db
|
|
113
|
+
.prepare("SELECT id, account, reading_json, created_at FROM plan_limit_readings WHERE account = ? AND project_id != ? AND created_at >= ?")
|
|
114
|
+
.all(account, projectId, since)) {
|
|
115
|
+
const row = readingRow.parse(raw);
|
|
116
|
+
const stored = parseReading(row.reading_json);
|
|
117
|
+
if (stored !== undefined)
|
|
118
|
+
readings.push(readingOf(stored, stored.usageId, row.created_at, byId));
|
|
119
|
+
}
|
|
120
|
+
for (const raw of standalone) {
|
|
121
|
+
const row = readingRow.parse(raw);
|
|
122
|
+
const stored = row.account === account ? parseReading(row.reading_json) : undefined;
|
|
123
|
+
if (stored !== undefined)
|
|
124
|
+
readings.push(readingOf(stored, row.id, row.created_at, byId));
|
|
125
|
+
}
|
|
126
|
+
return { byId, readings };
|
|
127
|
+
}
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
// How much of a CLI plan's windows a project's calls took. A plan's readings are account-wide:
|
|
2
|
+
// they move with every use of the account, this project's or not. So a call is charged only
|
|
3
|
+
// its own before→after rise, and only when nothing else on the account ran during it; calls
|
|
4
|
+
// that overlapped split their rise by tokens; anything else is said to be shared, not guessed.
|
|
5
|
+
// "measured": every call's rise was its own. "split": some overlapped and split their rise by
|
|
6
|
+
// tokens. "shared": some calls' share could not be told apart from other use. "approximate":
|
|
7
|
+
// stored before each call's own readings were kept, so only the account's rise is known.
|
|
8
|
+
export const windowBases = ["measured", "split", "shared", "approximate"];
|
|
9
|
+
// An after-only call (Claude Code reports its windows once) takes the account's reading just
|
|
10
|
+
// before it as its "before"; one older than this says nothing about the call alone.
|
|
11
|
+
export const beforeGapMs = 15 * 60_000;
|
|
12
|
+
// A stated reset that moves by less than this is drift, not a new window.
|
|
13
|
+
const resetDriftMs = 60 * 60_000;
|
|
14
|
+
const kindOrder = ["five_hour", "weekly", "other"];
|
|
15
|
+
const windowOf = (windows, kind) => windows?.find((window) => window.kind === kind);
|
|
16
|
+
export function windowUse(own, account = { calls: [], readings: [] }) {
|
|
17
|
+
const kinds = new Set();
|
|
18
|
+
for (const reading of own)
|
|
19
|
+
for (const window of [...(reading.before ?? []), ...(reading.after ?? [])])
|
|
20
|
+
kinds.add(window.kind);
|
|
21
|
+
return kindOrder.flatMap((kind) => {
|
|
22
|
+
if (!kinds.has(kind))
|
|
23
|
+
return [];
|
|
24
|
+
const use = kindUse(kind, own, account);
|
|
25
|
+
return use === undefined ? [] : [use];
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
function kindUse(kind, own, account) {
|
|
29
|
+
const mine = own.filter((reading) => windowOf(reading.before, kind) !== undefined || windowOf(reading.after, kind) !== undefined);
|
|
30
|
+
const legacy = mine.some((reading) => reading.usageId === null);
|
|
31
|
+
const calls = new Map();
|
|
32
|
+
for (const call of account.calls)
|
|
33
|
+
calls.set(call.id, call);
|
|
34
|
+
for (const reading of own)
|
|
35
|
+
if (reading.usageId !== null)
|
|
36
|
+
calls.set(reading.usageId, {
|
|
37
|
+
id: reading.usageId,
|
|
38
|
+
startMs: reading.startMs,
|
|
39
|
+
endMs: reading.endMs,
|
|
40
|
+
tokens: reading.tokens,
|
|
41
|
+
});
|
|
42
|
+
const everyReading = [...own, ...account.readings];
|
|
43
|
+
const points = [];
|
|
44
|
+
let used = 0;
|
|
45
|
+
let measured = 0;
|
|
46
|
+
let split = 0;
|
|
47
|
+
let shared = 0;
|
|
48
|
+
for (const reading of mine) {
|
|
49
|
+
const before = windowOf(reading.before, kind);
|
|
50
|
+
const after = windowOf(reading.after, kind);
|
|
51
|
+
if (before !== undefined)
|
|
52
|
+
points.push({ atMs: reading.startMs, window: before });
|
|
53
|
+
if (after !== undefined)
|
|
54
|
+
points.push({ atMs: reading.endMs, window: after });
|
|
55
|
+
if (legacy || reading.usageId === null)
|
|
56
|
+
continue;
|
|
57
|
+
const start = before === undefined ? pointBefore(kind, reading, everyReading) : undefined;
|
|
58
|
+
if (start !== undefined)
|
|
59
|
+
points.push(start);
|
|
60
|
+
const from = before ?? start?.window;
|
|
61
|
+
const spanStart = before === undefined ? start?.atMs : reading.startMs;
|
|
62
|
+
if (from === undefined || after === undefined || spanStart === undefined) {
|
|
63
|
+
shared += 1;
|
|
64
|
+
continue;
|
|
65
|
+
}
|
|
66
|
+
const rise = rolledOver(from, after, reading.endMs)
|
|
67
|
+
? after.usedPercent
|
|
68
|
+
: after.usedPercent - from.usedPercent;
|
|
69
|
+
const beside = [...calls.values()].filter((call) => call.id !== reading.usageId && call.endMs > spanStart && call.startMs < reading.endMs);
|
|
70
|
+
if (beside.length === 0) {
|
|
71
|
+
used += rise;
|
|
72
|
+
measured += 1;
|
|
73
|
+
continue;
|
|
74
|
+
}
|
|
75
|
+
const tokens = reading.tokens ?? 0;
|
|
76
|
+
const others = beside.map((call) => call.tokens);
|
|
77
|
+
if (tokens <= 0 || others.some((count) => count === null)) {
|
|
78
|
+
shared += 1;
|
|
79
|
+
continue;
|
|
80
|
+
}
|
|
81
|
+
const total = others.reduce((sum, count) => sum + (count ?? 0), tokens);
|
|
82
|
+
used += (rise * tokens) / total;
|
|
83
|
+
split += 1;
|
|
84
|
+
}
|
|
85
|
+
const sorted = points.toSorted((left, right) => left.atMs - right.atMs);
|
|
86
|
+
const first = sorted[0];
|
|
87
|
+
const last = sorted.at(-1);
|
|
88
|
+
if (first === undefined || last === undefined)
|
|
89
|
+
return undefined;
|
|
90
|
+
const { rise: accountPercent, segments } = accountRise(sorted);
|
|
91
|
+
const share = legacy
|
|
92
|
+
? accountPercent
|
|
93
|
+
: measured + split === 0
|
|
94
|
+
? null
|
|
95
|
+
: Math.min(Math.max(0, used), accountPercent);
|
|
96
|
+
return {
|
|
97
|
+
kind,
|
|
98
|
+
usedPercent: share,
|
|
99
|
+
basis: legacy ? "approximate" : shared > 0 ? "shared" : split > 0 ? "split" : "measured",
|
|
100
|
+
sharedCalls: legacy ? 0 : shared,
|
|
101
|
+
fromPercent: first.window.usedPercent,
|
|
102
|
+
accountPercent,
|
|
103
|
+
rolledOver: segments > 1,
|
|
104
|
+
nowPercent: last.window.usedPercent,
|
|
105
|
+
resetsAt: last.window.resetsAt,
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
// The latest reading on the account at or before the call began, from any call but itself.
|
|
109
|
+
function pointBefore(kind, call, readings) {
|
|
110
|
+
let found;
|
|
111
|
+
for (const reading of readings) {
|
|
112
|
+
if (reading.usageId !== null && reading.usageId === call.usageId)
|
|
113
|
+
continue;
|
|
114
|
+
const candidates = [];
|
|
115
|
+
const before = windowOf(reading.before, kind);
|
|
116
|
+
const after = windowOf(reading.after, kind);
|
|
117
|
+
if (before !== undefined)
|
|
118
|
+
candidates.push({ atMs: reading.startMs, window: before });
|
|
119
|
+
if (after !== undefined)
|
|
120
|
+
candidates.push({ atMs: reading.endMs, window: after });
|
|
121
|
+
for (const point of candidates)
|
|
122
|
+
if (point.atMs <= call.startMs &&
|
|
123
|
+
point.atMs >= call.startMs - beforeGapMs &&
|
|
124
|
+
(found === undefined || point.atMs > found.atMs))
|
|
125
|
+
found = point;
|
|
126
|
+
}
|
|
127
|
+
return found;
|
|
128
|
+
}
|
|
129
|
+
// Within one call the window reset when the reading dropped or the stated reset passed.
|
|
130
|
+
function rolledOver(before, after, endMs) {
|
|
131
|
+
if (after.usedPercent < before.usedPercent)
|
|
132
|
+
return true;
|
|
133
|
+
return before.resetsAt !== null && Date.parse(before.resetsAt) <= endMs;
|
|
134
|
+
}
|
|
135
|
+
// Across many calls' readings a drop alone is not a reset: parallel calls report out of
|
|
136
|
+
// order. A new window needs the stated reset to have passed, or a drop with the reset moved
|
|
137
|
+
// on by more than drift. The rise is counted per window from its readings' range, at most 100.
|
|
138
|
+
function accountRise(points) {
|
|
139
|
+
let segments = 1;
|
|
140
|
+
let rise = 0;
|
|
141
|
+
let base = points[0]?.window.usedPercent ?? 0;
|
|
142
|
+
let peak = base;
|
|
143
|
+
for (let at = 1; at < points.length; at += 1) {
|
|
144
|
+
const previous = points[at - 1];
|
|
145
|
+
const current = points[at];
|
|
146
|
+
if (previous === undefined || current === undefined)
|
|
147
|
+
continue;
|
|
148
|
+
if (newWindow(previous, current)) {
|
|
149
|
+
rise += Math.max(0, peak - base);
|
|
150
|
+
segments += 1;
|
|
151
|
+
base = 0;
|
|
152
|
+
peak = current.window.usedPercent;
|
|
153
|
+
continue;
|
|
154
|
+
}
|
|
155
|
+
peak = Math.max(peak, current.window.usedPercent);
|
|
156
|
+
}
|
|
157
|
+
rise += Math.max(0, peak - base);
|
|
158
|
+
return { rise: Math.min(100, rise), segments };
|
|
159
|
+
}
|
|
160
|
+
function newWindow(previous, current) {
|
|
161
|
+
const passed = previous.window.resetsAt !== null && Date.parse(previous.window.resetsAt) <= current.atMs;
|
|
162
|
+
if (passed)
|
|
163
|
+
return true;
|
|
164
|
+
if (current.window.usedPercent >= previous.window.usedPercent)
|
|
165
|
+
return false;
|
|
166
|
+
return (previous.window.resetsAt !== null &&
|
|
167
|
+
current.window.resetsAt !== null &&
|
|
168
|
+
Date.parse(current.window.resetsAt) - Date.parse(previous.window.resetsAt) > resetDriftMs);
|
|
169
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { isLocalCliProvider, systemVoiceProvider } from "../settings/model.js";
|
|
1
|
+
import { isLocalCliProvider, isSampleStandIn, systemVoiceProvider } from "../settings/model.js";
|
|
2
2
|
// Per million tokens; cached input at its own rate when the catalogue gives one.
|
|
3
3
|
export function tokenCost(rates, tokensIn, tokensOut, cachedTokens = 0) {
|
|
4
4
|
if (rates.inputPerMillionTokens === undefined || rates.outputPerMillionTokens === undefined)
|
|
@@ -57,8 +57,8 @@ export function priceCall(call, catalogue) {
|
|
|
57
57
|
price: api === undefined ? null : { catalogue: date, model: api.id, ...api.pricing },
|
|
58
58
|
};
|
|
59
59
|
}
|
|
60
|
-
// The computer's own voice
|
|
61
|
-
if (call.provider === systemVoiceProvider)
|
|
60
|
+
// The computer's own voice and the samples' stand-ins cost nothing.
|
|
61
|
+
if (call.provider === systemVoiceProvider || isSampleStandIn(call.provider))
|
|
62
62
|
return { onPlan: false, cost: 0, apiModel: null, apiCost: null, price: null };
|
|
63
63
|
const family = call.kind === "video" ? "image" : call.kind;
|
|
64
64
|
const entry = catalogue[family].find((model) => model.provider === call.provider && model.id === call.model);
|
|
@@ -2,6 +2,7 @@ import { z } from "zod";
|
|
|
2
2
|
import { planAccountNames, planAccounts, } from "../../kernel/ports/plan-limits.js";
|
|
3
3
|
import { defaultChannelId } from "../channels/model.js";
|
|
4
4
|
import { projectChannels } from "../channels/repo.js";
|
|
5
|
+
import { notTemplateSample, templateSampleProjects } from "../template-studio/repo.js";
|
|
5
6
|
const usageRow = z.object({
|
|
6
7
|
project_id: z.string(),
|
|
7
8
|
on_plan: z.number(),
|
|
@@ -42,13 +43,30 @@ export function weekSummary(db, since, channelId) {
|
|
|
42
43
|
if (row.on_plan === 1)
|
|
43
44
|
apiEquivalent = (apiEquivalent ?? 0) + (row.api_cost ?? 0);
|
|
44
45
|
};
|
|
46
|
+
let studioCost = 0;
|
|
47
|
+
const hidden = templateSampleProjects(db);
|
|
45
48
|
for (const raw of db
|
|
46
49
|
.prepare("SELECT project_id, on_plan, cost, api_cost FROM provider_usage WHERE created_at >= ?")
|
|
47
50
|
.all(since)) {
|
|
48
51
|
const row = usageRow.parse(raw);
|
|
49
|
-
if (
|
|
50
|
-
|
|
52
|
+
if (hidden.has(row.project_id)) {
|
|
53
|
+
if (channelId !== undefined)
|
|
54
|
+
continue;
|
|
55
|
+
studioCost += row.cost ?? 0;
|
|
56
|
+
}
|
|
57
|
+
else if (!projectInScope(row.project_id))
|
|
58
|
+
continue;
|
|
59
|
+
count(row);
|
|
51
60
|
}
|
|
61
|
+
// A sample or bake-off copy removed since keeps what it was charged here.
|
|
62
|
+
if (channelId === undefined)
|
|
63
|
+
for (const raw of db
|
|
64
|
+
.prepare("SELECT cost FROM template_studio_spend WHERE created_at >= ?")
|
|
65
|
+
.all(since)) {
|
|
66
|
+
const spent = z.object({ cost: z.number() }).parse(raw).cost;
|
|
67
|
+
studioCost += spent;
|
|
68
|
+
count({ channel_id: "", on_plan: 0, cost: spent, api_cost: null });
|
|
69
|
+
}
|
|
52
70
|
for (const raw of db
|
|
53
71
|
.prepare("SELECT channel_id, on_plan, cost, api_cost FROM standalone_usage WHERE created_at >= ?")
|
|
54
72
|
.all(since)) {
|
|
@@ -59,11 +77,21 @@ export function weekSummary(db, since, channelId) {
|
|
|
59
77
|
count(row);
|
|
60
78
|
}
|
|
61
79
|
// A video is made when its file lands; a remade video counts again, as it was made again.
|
|
80
|
+
// A template's studio sample is no video of the person's (its spend still counts above).
|
|
62
81
|
const videos = db
|
|
63
|
-
.prepare(
|
|
82
|
+
.prepare(`SELECT project_id FROM outputs WHERE role = 'video' AND created_at >= ? AND ${notTemplateSample("outputs", "project_id")}`)
|
|
64
83
|
.all(since)
|
|
65
84
|
.filter((row) => projectInScope(String(row.project_id))).length;
|
|
66
|
-
return {
|
|
85
|
+
return {
|
|
86
|
+
since,
|
|
87
|
+
videos,
|
|
88
|
+
calls,
|
|
89
|
+
cost,
|
|
90
|
+
unpriced,
|
|
91
|
+
studioCost,
|
|
92
|
+
apiEquivalent,
|
|
93
|
+
plans: planStandings(db),
|
|
94
|
+
};
|
|
67
95
|
}
|
|
68
96
|
function knownChannels(db) {
|
|
69
97
|
return new Set(db
|
|
@@ -7,9 +7,11 @@ import { sampleIds } from "../onboarding/model.js";
|
|
|
7
7
|
import { readSampleRecord } from "../onboarding/state.js";
|
|
8
8
|
import { projectHolds } from "../reviews/holds.js";
|
|
9
9
|
import { limitWaitsByProject, listingWait } from "../run-cost/panel.js";
|
|
10
|
+
import { notTemplateSample } from "../template-studio/repo.js";
|
|
10
11
|
import { setAsideProjects, uploadedAndSettled, uploadedProjects } from "../uploads/repo.js";
|
|
11
12
|
import { settledStatus } from "../uploads/rules.js";
|
|
12
13
|
import { nextOccurrence } from "./calendar.js";
|
|
14
|
+
import { upcomingDesignRuns } from "./design-agenda.js";
|
|
13
15
|
import { preparedProject } from "./prepare.js";
|
|
14
16
|
import { scheduleRows } from "./repo.js";
|
|
15
17
|
import { calendarMaxDays, } from "./schema.js";
|
|
@@ -49,6 +51,8 @@ function upcomingRuns(deps, schedule, now, from, to) {
|
|
|
49
51
|
return [];
|
|
50
52
|
if (schedule.status !== "active" && schedule.status !== "paused")
|
|
51
53
|
return [];
|
|
54
|
+
if (schedule.cadence.kind === "by-date")
|
|
55
|
+
return upcomingDesignRuns(deps.db, schedule, now, from, to);
|
|
52
56
|
const paused = schedule.status === "paused";
|
|
53
57
|
// A paused schedule resumes from the next occurrence after the moment Resume is pressed.
|
|
54
58
|
let at = schedule.nextRunAt === null
|
|
@@ -58,6 +62,7 @@ function upcomingRuns(deps, schedule, now, from, to) {
|
|
|
58
62
|
: new Date(schedule.nextRunAt);
|
|
59
63
|
const template = deps.template(schedule.templateId);
|
|
60
64
|
const templateName = template?.name ?? null;
|
|
65
|
+
const notReady = deps.notReady?.(schedule.templateId) !== undefined;
|
|
61
66
|
const form = template?.document.form;
|
|
62
67
|
const { items } = schedule;
|
|
63
68
|
const generation = schedule.topicGeneration.mode;
|
|
@@ -91,10 +96,13 @@ function upcomingRuns(deps, schedule, now, from, to) {
|
|
|
91
96
|
? "held"
|
|
92
97
|
: "generated",
|
|
93
98
|
renderedTitle: title,
|
|
99
|
+
templateKind: "project",
|
|
100
|
+
timezone: schedule.timezone,
|
|
94
101
|
// Prepared ahead (`prepare.ts`): the project the day continues.
|
|
95
102
|
prepared: title === null || queuedTopic === undefined
|
|
96
103
|
? null
|
|
97
104
|
: (preparedProject(deps.db, schedule.id, title)?.projectId ?? null),
|
|
105
|
+
notReady,
|
|
98
106
|
});
|
|
99
107
|
if (schedule.cadence.kind === "once")
|
|
100
108
|
break;
|
|
@@ -186,7 +194,8 @@ function projectsIn(deps, from, to) {
|
|
|
186
194
|
COALESCE(project_controls.paused,0) AS paused,max(stages.finished_at) AS finished_at
|
|
187
195
|
FROM projects LEFT JOIN stages ON stages.project_id=projects.id
|
|
188
196
|
LEFT JOIN project_controls ON project_controls.project_id=projects.id
|
|
189
|
-
WHERE projects.created_at<? AND ${liveProject()}
|
|
197
|
+
WHERE projects.created_at<? AND ${liveProject()} AND ${notTemplateSample()}
|
|
198
|
+
GROUP BY projects.id
|
|
190
199
|
HAVING COALESCE(project_controls.paused,0)=1 OR count(stages.id)=0
|
|
191
200
|
OR sum(stages.state IN ('pending','running'))>0
|
|
192
201
|
OR COALESCE(max(stages.finished_at),projects.created_at)>=?
|
|
@@ -244,7 +253,7 @@ function queued(deps) {
|
|
|
244
253
|
return [];
|
|
245
254
|
const row = deps.db
|
|
246
255
|
.prepare(`SELECT projects.title, batches.created_at FROM projects, batches
|
|
247
|
-
WHERE projects.id=? AND batches.id=? AND ${liveProject()}`)
|
|
256
|
+
WHERE projects.id=? AND batches.id=? AND ${liveProject()} AND ${notTemplateSample()}`)
|
|
248
257
|
.get(entry.projectId, entry.batchId);
|
|
249
258
|
if (row === undefined)
|
|
250
259
|
return [];
|