@gentbajko/slopify 2.5.0 → 3.0.1
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/LICENSE +201 -21
- package/NOTICE +12 -0
- package/README.md +24 -41
- package/SUBTITLES.md +1 -1
- package/dist/adapter-registry.js +31 -0
- package/dist/adapters/alignment/cache.js +4 -1
- package/dist/adapters/alignment/ctc.js +25 -23
- package/dist/adapters/alignment/index.js +37 -6
- package/dist/adapters/alignment/multilingual.js +267 -0
- package/dist/adapters/alignment/protocol.js +11 -0
- package/dist/adapters/alignment/quality.js +4 -3
- package/dist/adapters/alignment/sentences.js +95 -0
- package/dist/adapters/alignment/spec.js +24 -0
- package/dist/adapters/alignment/spell.js +769 -0
- package/dist/adapters/alignment/text.js +56 -9
- package/dist/adapters/alignment/window.js +34 -30
- package/dist/adapters/alignment/worker.js +23 -21
- package/dist/adapters/explain.js +11 -11
- package/dist/adapters/host-cli/index.js +44 -5
- package/dist/adapters/host-cli/transport.js +7 -5
- package/dist/adapters/image/bytes.js +2 -2
- package/dist/adapters/image/codex-output.js +1 -1
- package/dist/adapters/image/codex.js +87 -22
- package/dist/adapters/image/fal.js +13 -5
- package/dist/adapters/image/google.js +11 -8
- package/dist/adapters/image/openai.js +14 -7
- package/dist/adapters/image/reference.js +34 -0
- package/dist/adapters/image/replicate.js +7 -2
- package/dist/adapters/image/video.js +2 -2
- package/dist/adapters/key-probes.js +85 -0
- package/dist/adapters/llm/claude-code.js +168 -32
- package/dist/adapters/llm/cli-login-error.js +1 -1
- package/dist/adapters/llm/codex-limits.js +157 -0
- package/dist/adapters/llm/codex.js +99 -12
- package/dist/adapters/llm/document-workspace.js +2 -2
- package/dist/adapters/llm/gemini-workspace.js +1 -1
- package/dist/adapters/llm/gemini.js +16 -3
- package/dist/adapters/llm/image-workspace.js +58 -0
- package/dist/adapters/llm/openrouter.js +11 -3
- package/dist/adapters/llm/run-cli.js +12 -7
- package/dist/adapters/tts/cartesia.js +10 -0
- package/dist/adapters/tts/elevenlabs.js +31 -9
- package/dist/adapters/tts/gemini.js +206 -0
- package/dist/adapters/tts/inworld-async.js +9 -5
- package/dist/adapters/tts/inworld.js +17 -3
- package/dist/adapters/tts/openai.js +3 -0
- package/dist/adapters/tts/pcm-mp3.js +62 -0
- package/dist/adapters/tts/system.js +147 -0
- package/dist/adapters/tts/voice-languages.js +80 -0
- package/dist/assets/fonts/noto/NotoSans-Regular.ttf +0 -0
- package/dist/assets/fonts/noto/NotoSansArabic-Regular.ttf +0 -0
- package/dist/assets/fonts/noto/NotoSansDevanagari-Regular.ttf +0 -0
- package/dist/assets/fonts/noto/NotoSansHebrew-Regular.ttf +0 -0
- package/dist/assets/fonts/noto/NotoSansThai-Regular.ttf +0 -0
- package/dist/assets/fonts/noto/OFL.txt +93 -0
- package/dist/assets/fonts/noto/SOURCE.txt +18 -0
- package/dist/assets/models.yaml +38 -0
- package/dist/assets/sample/sample-audiobook.tar +0 -0
- package/dist/assets/sample/sample-podcast.tar +0 -0
- package/dist/assets/sample/sample-project.tar +0 -0
- package/dist/assets/style-preview/narration.mp3 +0 -0
- package/dist/assets/style-preview/tall-1.jpg +0 -0
- package/dist/assets/style-preview/tall-2.jpg +0 -0
- package/dist/assets/style-preview/tall-3.jpg +0 -0
- package/dist/assets/style-preview/wide-1.jpg +0 -0
- package/dist/assets/style-preview/wide-2.jpg +0 -0
- package/dist/assets/style-preview/wide-3.jpg +0 -0
- package/dist/catalog/merge.js +132 -0
- package/dist/catalog/registry.js +17 -14
- package/dist/catalog/schema.js +2 -0
- package/dist/catalog/store.js +147 -8
- package/dist/catalog/validate.js +2 -2
- package/dist/compose.yaml +71 -0
- package/dist/edge/autostart/docker-record.js +32 -0
- package/dist/edge/autostart/docker.js +118 -0
- package/dist/edge/autostart/index.js +78 -0
- package/dist/edge/autostart/launcher.js +176 -0
- package/dist/edge/autostart/model.js +3 -0
- package/dist/edge/autostart/native.js +210 -0
- package/dist/edge/autostart/prompt.js +72 -0
- package/dist/edge/autostart/service.js +90 -0
- package/dist/edge/cli-args.js +71 -0
- package/dist/edge/cli.js +64 -63
- package/dist/edge/docker-install/activation.js +56 -0
- package/dist/edge/docker-install/apply.js +458 -0
- package/dist/edge/docker-install/engine.js +362 -0
- package/dist/edge/docker-install/run.js +160 -0
- package/dist/edge/docker-install/state.js +146 -0
- package/dist/edge/{docker-projects → docker-install}/volume.js +1 -5
- package/dist/edge/docker.js +14 -22
- package/dist/edge/events/hub.js +3 -1
- package/dist/edge/host-cli.js +1 -1
- package/dist/edge/http/actions.js +38 -11
- package/dist/edge/http/app.js +60 -2
- package/dist/edge/http/auditions.js +70 -0
- package/dist/edge/http/autostart.js +37 -0
- package/dist/edge/http/byte-range.js +42 -0
- package/dist/edge/http/channel-memory.js +92 -0
- package/dist/edge/http/channels.js +128 -0
- package/dist/edge/http/diagnostics.js +1 -0
- package/dist/edge/http/draft-files.js +2 -4
- package/dist/edge/http/entries.js +22 -3
- package/dist/edge/http/files.js +11 -7
- package/dist/edge/http/folder-location.js +1 -1
- package/dist/edge/http/home.js +39 -0
- package/dist/edge/http/host-cli.js +29 -5
- package/dist/edge/http/narration-peaks.js +76 -0
- package/dist/edge/http/onboarding.js +212 -0
- package/dist/edge/http/open-folder.js +1 -1
- package/dist/edge/http/patch-notes.js +45 -0
- package/dist/edge/http/planning.js +4 -2
- package/dist/edge/http/project-create.js +87 -0
- package/dist/edge/http/project-templates.js +19 -2
- package/dist/edge/http/projects.js +37 -100
- package/dist/edge/http/prompts.js +40 -3
- package/dist/edge/http/pronunciations.js +23 -4
- package/dist/edge/http/providers.js +119 -3
- package/dist/edge/http/reviews.js +131 -0
- package/dist/edge/http/revision-files.js +3 -5
- package/dist/edge/http/revisions.js +1 -1
- package/dist/edge/http/run-cost.js +25 -0
- package/dist/edge/http/schedules.js +115 -6
- package/dist/edge/http/settings.js +84 -6
- package/dist/edge/http/storage-files.js +50 -0
- package/dist/edge/http/storage.js +43 -2
- package/dist/edge/http/studio.js +287 -0
- package/dist/edge/http/style-preview.js +61 -0
- package/dist/edge/http/subtitles.js +1 -1
- package/dist/edge/http/trash.js +69 -0
- package/dist/edge/http/tutorials.js +59 -0
- package/dist/edge/http/update.js +24 -3
- package/dist/edge/http/whats-new.js +15 -0
- package/dist/edge/http/youtube-edits.js +92 -0
- package/dist/edge/native-update.js +62 -0
- package/dist/extension/slopify-studio-chrome.zip +0 -0
- package/dist/extension/slopify-studio-firefox.zip +0 -0
- package/dist/host-cli/gemini-login.js +83 -0
- package/dist/host-cli/open-folder.js +10 -10
- package/dist/host-cli/runtime.js +9 -1
- package/dist/host-cli/status.js +4 -2
- package/dist/kernel/db/migrations/0025-automatic-reviews.sql +40 -0
- package/dist/kernel/db/migrations/0026-schedule-topic-generation.sql +23 -0
- package/dist/kernel/db/migrations/0027-run-cost.sql +58 -0
- package/dist/kernel/db/migrations/0030-automatic-retries.sql +18 -0
- package/dist/kernel/db/migrations/0031-prompt-history-and-description-edits.sql +33 -0
- package/dist/kernel/db/migrations/0032-channels.sql +65 -0
- package/dist/kernel/db/migrations/0034-script-prompts.sql +23 -0
- package/dist/kernel/db/migrations/0035-narration-aliases.sql +13 -0
- package/dist/kernel/db/migrations/0037-project-uploads.sql +8 -0
- package/dist/kernel/db/migrations/0038-voice-languages.sql +7 -0
- package/dist/kernel/db/migrations/0039-channel-essentials.sql +64 -0
- package/dist/kernel/db/migrations/0040-standalone-usage.sql +35 -0
- package/dist/kernel/db/migrations/0041-cast-hosts.sql +3 -0
- package/dist/kernel/paths.js +27 -6
- package/dist/kernel/pipeline.js +4 -1
- package/dist/kernel/ports/host-cli.js +122 -14
- package/dist/kernel/ports/key-probe.js +1 -0
- package/dist/kernel/ports/languages.js +157 -0
- package/dist/kernel/ports/model.js +10 -5
- package/dist/kernel/ports/narration-aliases.js +60 -0
- package/dist/kernel/ports/plan-limits.js +36 -0
- package/dist/kernel/ports/system-speech.js +203 -0
- package/dist/kernel/runner/attempt.js +17 -8
- package/dist/kernel/runner/audition.js +36 -0
- package/dist/kernel/runner/graph.js +40 -5
- package/dist/kernel/runner/index.js +39 -9
- package/dist/kernel/runner/meter.js +1 -0
- package/dist/kernel/runner/providers.js +142 -7
- package/dist/kernel/runner/retry-policy.js +43 -0
- package/dist/kernel/runner/standalone.js +134 -0
- package/dist/kernel/runner/work-authority.js +28 -2
- package/dist/main.js +277 -16
- package/dist/model-catalog.js +2 -0
- package/dist/patch-notes/0.4.2-to-1.0.md +93 -0
- package/dist/patch-notes/1.0-to-2.0.md +74 -0
- package/dist/patch-notes/2.0-to-3.0.md +257 -0
- package/dist/patch-notes/3.0.0.md +70 -0
- package/dist/patch-notes/3.0.1.md +105 -0
- package/dist/patch-notes/index.json +32 -0
- package/dist/slices/admission/model.js +27 -0
- package/dist/slices/admission/repo.js +24 -4
- package/dist/slices/admission/rules.js +80 -2
- package/dist/slices/admission/schema.js +58 -1
- package/dist/slices/admission/short-mode.js +47 -0
- package/dist/slices/admission/start.js +42 -6
- package/dist/slices/article/continuation.js +2 -2
- package/dist/slices/article/provided-entries.js +2 -2
- package/dist/slices/article/run.js +6 -2
- package/dist/slices/article/segments.js +3 -3
- package/dist/slices/backups/folder.js +14 -6
- package/dist/slices/batch/index.js +7 -2
- package/dist/slices/cancel/index.js +1 -1
- package/dist/slices/channels/cast-images.js +110 -0
- package/dist/slices/channels/cast-match.js +32 -0
- package/dist/slices/channels/images.js +19 -0
- package/dist/slices/channels/model.js +5 -0
- package/dist/slices/channels/rebrand.js +100 -0
- package/dist/slices/channels/repo.js +221 -0
- package/dist/slices/channels/runs.js +129 -0
- package/dist/slices/channels/schema.js +152 -0
- package/dist/slices/channels/service.js +180 -0
- package/dist/slices/channels/studio-csv.js +110 -0
- package/dist/slices/channels/videos.js +120 -0
- package/dist/slices/checkpoints/recovery.js +10 -0
- package/dist/slices/control/index.js +5 -2
- package/dist/slices/control/providers.js +4 -1
- package/dist/slices/document/fonts.js +1 -1
- package/dist/slices/document/render.js +1 -1
- package/dist/slices/episodes/related.js +90 -0
- package/dist/slices/episodes/repo.js +60 -0
- package/dist/slices/episodes/service.js +58 -0
- package/dist/slices/episodes/summarize.js +170 -0
- package/dist/slices/estimate/index.js +109 -28
- package/dist/slices/estimate/requests.js +68 -2
- package/dist/slices/eta/history.js +56 -0
- package/dist/slices/eta/model.js +63 -0
- package/dist/slices/eta/view.js +25 -0
- package/dist/slices/fixes/rules.js +73 -0
- package/dist/slices/fonts/coverage.js +82 -0
- package/dist/slices/fonts/sfnt.js +74 -0
- package/dist/slices/images/run.js +8 -5
- package/dist/slices/images/scale.js +84 -0
- package/dist/slices/library/diff.js +122 -0
- package/dist/slices/library/history.js +100 -0
- package/dist/slices/library/model.js +3 -0
- package/dist/slices/library/photorealistic.js +49 -0
- package/dist/slices/library/repo.js +19 -14
- package/dist/slices/library/save.js +62 -12
- package/dist/slices/library/slots.js +7 -1
- package/dist/slices/library/used-by.js +153 -0
- package/dist/slices/loudness/level-pieces.js +41 -0
- package/dist/slices/loudness/loudnorm.js +164 -0
- package/dist/slices/loudness/model.js +153 -0
- package/dist/slices/model-upkeep/model.js +10 -0
- package/dist/slices/model-upkeep/switch.js +217 -0
- package/dist/slices/model-upkeep/usage.js +185 -0
- package/dist/slices/narration/aliases-library.js +31 -0
- package/dist/slices/narration/aliases-schema.js +53 -0
- package/dist/slices/narration/aliases.js +49 -0
- package/dist/slices/narration/blocks.js +352 -0
- package/dist/slices/narration/chunk.js +19 -7
- package/dist/slices/narration/concat.js +2 -2
- package/dist/slices/narration/describe.js +45 -0
- package/dist/slices/narration/pauses-model.js +63 -0
- package/dist/slices/narration/pauses.js +269 -0
- package/dist/slices/narration/peaks-model.js +1 -0
- package/dist/slices/narration/peaks.js +94 -0
- package/dist/slices/narration/preparation.js +21 -3
- package/dist/slices/narration/pronunciation-chunks.js +3 -3
- package/dist/slices/narration/pronunciation.js +72 -25
- package/dist/slices/narration/run.js +6 -6
- package/dist/slices/narration/shared-glossary.js +1 -1
- package/dist/slices/narration/steering.js +15 -4
- package/dist/slices/notifications/notifier.js +47 -3
- package/dist/slices/notifications/rules.js +48 -3
- package/dist/slices/onboarding/copy.js +143 -0
- package/dist/slices/onboarding/first-run.js +72 -0
- package/dist/slices/onboarding/full-video.js +81 -0
- package/dist/slices/onboarding/install.js +119 -0
- package/dist/slices/onboarding/model.js +85 -0
- package/dist/slices/onboarding/packs.js +491 -0
- package/dist/slices/onboarding/quick-short.js +144 -0
- package/dist/slices/onboarding/sample.js +132 -0
- package/dist/slices/onboarding/state.js +68 -0
- package/dist/slices/onboarding/template.js +73 -0
- package/dist/slices/patch-notes/library.js +73 -0
- package/dist/slices/patch-notes/seen.js +77 -0
- package/dist/slices/play-drafts/convert.js +127 -8
- package/dist/slices/play-drafts/readiness.js +4 -1
- package/dist/slices/play-drafts/repo.js +2 -0
- package/dist/slices/play-drafts/review-inputs.js +20 -3
- package/dist/slices/play-drafts/schema.js +89 -0
- package/dist/slices/play-drafts/service.js +4 -1
- package/dist/slices/play-drafts/start.js +43 -18
- package/dist/slices/project-templates/from-project.js +59 -6
- package/dist/slices/project-templates/next-chapter.js +94 -0
- package/dist/slices/project-templates/one-off.js +26 -0
- package/dist/slices/project-templates/repo.js +2 -1
- package/dist/slices/project-templates/schema.js +7 -1
- package/dist/slices/project-templates/service.js +15 -4
- package/dist/slices/project-templates/setup.js +18 -2
- package/dist/slices/rebuild/admission-repo.js +1 -1
- package/dist/slices/rebuild/model.js +6 -0
- package/dist/slices/rebuild/preview-details.js +46 -3
- package/dist/slices/rebuild/preview-plan.js +13 -6
- package/dist/slices/rebuild/recipe-audio-parts.js +40 -7
- package/dist/slices/rebuild/recipe-audio.js +66 -12
- package/dist/slices/rebuild/recipe-build.js +8 -4
- package/dist/slices/rebuild/recipe-cast.js +18 -0
- package/dist/slices/rebuild/recipe-checkpoints.js +11 -9
- package/dist/slices/rebuild/recipe-describe.js +93 -0
- package/dist/slices/rebuild/recipe-edit.js +13 -1
- package/dist/slices/rebuild/recipe-exports.js +67 -8
- package/dist/slices/rebuild/recipe-input-schema.js +35 -0
- package/dist/slices/rebuild/recipe-legacy.js +9 -0
- package/dist/slices/rebuild/recipe-loudness.js +45 -0
- package/dist/slices/rebuild/recipe-model.js +25 -6
- package/dist/slices/rebuild/recipe-pauses.js +7 -0
- package/dist/slices/rebuild/recipe-preparation.js +3 -3
- package/dist/slices/rebuild/recipe-provider-choice.js +11 -1
- package/dist/slices/rebuild/recipe-reference.js +15 -1
- package/dist/slices/rebuild/recipe-reviews.js +117 -0
- package/dist/slices/rebuild/recipe-save.js +4 -1
- package/dist/slices/rebuild/recipe-shorts.js +23 -3
- package/dist/slices/rebuild/recipe-text.js +114 -19
- package/dist/slices/rebuild/recipe-validation.js +14 -2
- package/dist/slices/rebuild/recipe-visual.js +96 -33
- package/dist/slices/rebuild/recipe-voices.js +286 -0
- package/dist/slices/rebuild/recipe-work.js +77 -6
- package/dist/slices/rebuild/recipe-youtube.js +2 -0
- package/dist/slices/rebuild/recovery-conflict.js +7 -3
- package/dist/slices/rebuild/recovery-model.js +3 -0
- package/dist/slices/rebuild/recovery-selection.js +28 -2
- package/dist/slices/rebuild/recovery.js +35 -17
- package/dist/slices/rebuild/repo.js +3 -3
- package/dist/slices/rebuild/retry.js +18 -0
- package/dist/slices/rebuild/review-redo.js +125 -0
- package/dist/slices/rebuild/runtime-actions.js +5 -0
- package/dist/slices/rebuild/runtime-admission.js +4 -1
- package/dist/slices/rebuild/runtime-animate.js +4 -4
- package/dist/slices/rebuild/runtime-article.js +5 -5
- package/dist/slices/rebuild/runtime-document.js +4 -4
- package/dist/slices/rebuild/runtime-export-bed.js +43 -0
- package/dist/slices/rebuild/runtime-export-edit.js +70 -11
- package/dist/slices/rebuild/runtime-export-inputs.js +52 -10
- package/dist/slices/rebuild/runtime-export-short.js +85 -0
- package/dist/slices/rebuild/runtime-export.js +84 -19
- package/dist/slices/rebuild/runtime-figure-card.js +87 -0
- package/dist/slices/rebuild/runtime-image.js +21 -2
- package/dist/slices/rebuild/runtime-local.js +148 -24
- package/dist/slices/rebuild/runtime-materialize.js +8 -0
- package/dist/slices/rebuild/runtime-narration-reuse.js +2 -1
- package/dist/slices/rebuild/runtime-narration-text.js +7 -7
- package/dist/slices/rebuild/runtime-pictures.js +28 -0
- package/dist/slices/rebuild/runtime-piece-label.js +27 -0
- package/dist/slices/rebuild/runtime-provider.js +126 -18
- package/dist/slices/rebuild/runtime-publication.js +21 -5
- package/dist/slices/rebuild/runtime-review.js +267 -0
- package/dist/slices/rebuild/runtime-run.js +17 -11
- package/dist/slices/rebuild/runtime-shorts.js +44 -17
- package/dist/slices/rebuild/runtime-store.js +62 -11
- package/dist/slices/rebuild/runtime-subtitles.js +72 -19
- package/dist/slices/rebuild/runtime-voices.js +103 -0
- package/dist/slices/rebuild/runtime-youtube.js +7 -6
- package/dist/slices/rebuild/service-readiness.js +3 -3
- package/dist/slices/rebuild/soften.js +49 -0
- package/dist/slices/rebuild/transition-repo.js +9 -2
- package/dist/slices/research/run.js +4 -4
- package/dist/slices/research/synthesis.js +2 -2
- package/dist/slices/reviews/model.js +105 -0
- package/dist/slices/reviews/outcome.js +22 -0
- package/dist/slices/reviews/repo.js +118 -0
- package/dist/slices/reviews/rules.js +63 -0
- package/dist/slices/reviews/verdict.js +71 -0
- package/dist/slices/reviews/view.js +30 -0
- package/dist/slices/revisions/adopt-content.js +5 -2
- package/dist/slices/revisions/adopt.js +3 -1
- package/dist/slices/revisions/downloads.js +7 -1
- package/dist/slices/revisions/image-plan.js +183 -0
- package/dist/slices/revisions/mutation-assets.js +10 -0
- package/dist/slices/revisions/mutation-cues.js +71 -0
- package/dist/slices/revisions/mutation-request.js +2 -1
- package/dist/slices/revisions/mutations.js +97 -3
- package/dist/slices/revisions/schema.js +11 -1
- package/dist/slices/run-cost/limits.js +128 -0
- package/dist/slices/run-cost/meter.js +47 -0
- package/dist/slices/run-cost/panel.js +256 -0
- package/dist/slices/run-cost/pricing.js +95 -0
- package/dist/slices/run-cost/week.js +108 -0
- package/dist/slices/schedules/agenda.js +222 -0
- package/dist/slices/schedules/repo.js +58 -18
- package/dist/slices/schedules/scheduler.js +68 -17
- package/dist/slices/schedules/schema.js +179 -1
- package/dist/slices/schedules/service.js +43 -4
- package/dist/slices/schedules/similar.js +128 -0
- package/dist/slices/schedules/topic-list.js +163 -0
- package/dist/slices/schedules/topics.js +523 -0
- package/dist/slices/settings/first-run.js +106 -0
- package/dist/slices/settings/health.js +247 -0
- package/dist/slices/settings/key-guides.js +138 -0
- package/dist/slices/settings/key-test.js +93 -0
- package/dist/slices/settings/keys.js +10 -5
- package/dist/slices/settings/model-reach.js +52 -0
- package/dist/slices/settings/model.js +55 -1
- package/dist/slices/settings/models.js +5 -1
- package/dist/slices/settings/playback.js +15 -0
- package/dist/slices/settings/readiness.js +14 -2
- package/dist/slices/settings/repo.js +17 -3
- package/dist/slices/settings/tutorial-schema.js +5 -0
- package/dist/slices/settings/voices.js +64 -4
- package/dist/slices/settings/whats-new.js +64 -0
- package/dist/slices/shorts/captions.js +9 -0
- package/dist/slices/shorts/pick.js +3 -3
- package/dist/slices/shorts/prompts.js +1 -1
- package/dist/slices/shorts/render.js +63 -13
- package/dist/slices/storage/asset-name.js +3 -0
- package/dist/slices/storage/backup-export.js +48 -9
- package/dist/slices/storage/backup-format.js +38 -1
- package/dist/slices/storage/backup-import.js +191 -19
- package/dist/slices/storage/delete-project.js +7 -4
- package/dist/slices/storage/documents.js +87 -0
- package/dist/slices/storage/downloads.js +33 -1
- package/dist/slices/storage/files-location.js +531 -0
- package/dist/slices/storage/layout.js +14 -4
- package/dist/slices/storage/model.js +11 -0
- package/dist/slices/storage/portable.js +94 -12
- package/dist/slices/storage/reconcile.js +14 -1
- package/dist/slices/storage/repo.js +4 -1
- package/dist/slices/storage/schema.js +11 -0
- package/dist/slices/storage/trim.js +146 -0
- package/dist/slices/studio/disclosure.js +74 -0
- package/dist/slices/studio/model.js +30 -0
- package/dist/slices/studio/pack.js +211 -0
- package/dist/slices/studio/queue.js +69 -0
- package/dist/slices/studio/settings.js +170 -0
- package/dist/slices/style-preview/images.js +61 -0
- package/dist/slices/style-preview/narration.js +33 -0
- package/dist/slices/style-preview/render.js +148 -0
- package/dist/slices/style-preview/schema.js +59 -0
- package/dist/slices/style-preview/service.js +92 -0
- package/dist/slices/style-preview/settings.js +139 -0
- package/dist/slices/subtitles/captions.js +42 -9
- package/dist/slices/subtitles/model.js +22 -0
- package/dist/slices/subtitles/prepare.js +27 -16
- package/dist/slices/subtitles/transcript.js +1 -1
- package/dist/slices/thumbnail/run.js +7 -7
- package/dist/slices/trash/model.js +25 -0
- package/dist/slices/trash/service.js +273 -0
- package/dist/slices/tutorials/anchors.js +57 -0
- package/dist/slices/tutorials/library.js +185 -0
- package/dist/slices/uploads/repo.js +20 -0
- package/dist/slices/video/ambient-bed-schema.js +15 -0
- package/dist/slices/video/ambient-bed.js +95 -0
- package/dist/slices/video/ambient-mix.js +72 -0
- package/dist/slices/video/audio-inputs.js +2 -2
- package/dist/slices/video/cards.js +5 -2
- package/dist/slices/video/edit-list.js +23 -2
- package/dist/slices/video/edit-settings.js +13 -3
- package/dist/slices/video/ffmpeg.js +63 -9
- package/dist/slices/video/figure-card.js +492 -0
- package/dist/slices/video/figure-spans.js +51 -0
- package/dist/slices/video/plan.js +125 -13
- package/dist/slices/video/run.js +14 -12
- package/dist/slices/video/slideshow.js +39 -4
- package/dist/slices/voices/attribution.js +69 -0
- package/dist/slices/voices/audio-files.js +87 -0
- package/dist/slices/voices/audition.js +25 -0
- package/dist/slices/voices/cast.js +74 -0
- package/dist/slices/voices/grouping.js +59 -0
- package/dist/slices/voices/join.js +35 -0
- package/dist/slices/voices/languages.js +25 -0
- package/dist/slices/voices/model.js +218 -0
- package/dist/slices/voices/palette.js +26 -0
- package/dist/slices/voices/panel.js +91 -0
- package/dist/slices/voices/portraits.js +55 -0
- package/dist/slices/voices/script.js +136 -0
- package/dist/slices/voices/timing.js +58 -0
- package/dist/slices/youtube/answer.js +1 -1
- package/dist/slices/youtube/chapters.js +126 -0
- package/dist/slices/youtube/edits-repo.js +84 -0
- package/dist/slices/youtube/edits.js +89 -0
- package/dist/slices/youtube/placeholders.js +98 -0
- package/dist/slices/youtube/transcript.js +11 -4
- package/dist/tutorials/AI-CLIs.md +146 -0
- package/dist/tutorials/Backups.md +121 -0
- package/dist/tutorials/Calendar.md +103 -0
- package/dist/tutorials/Cast-Library.md +112 -0
- package/dist/tutorials/Channels.md +148 -0
- package/dist/tutorials/Costs-and-Run-Cost.md +136 -0
- package/dist/tutorials/Docker.md +171 -0
- package/dist/tutorials/Document-Themes.md +207 -0
- package/dist/tutorials/Editing-a-Project.md +204 -0
- package/dist/tutorials/FAQ.md +134 -0
- package/dist/tutorials/First-Launch-and-Welcome.md +153 -0
- package/dist/tutorials/Home-and-Projects.md +174 -0
- package/dist/tutorials/Home.md +119 -0
- package/dist/tutorials/Install.md +154 -0
- package/dist/tutorials/Intros-and-Outros.md +86 -0
- package/dist/tutorials/Keyboard-Shortcuts-and-Command-Palette.md +215 -0
- package/dist/tutorials/Library-Overview.md +75 -0
- package/dist/tutorials/Models.md +133 -0
- package/dist/tutorials/Multiple-Voices.md +161 -0
- package/dist/tutorials/Narration-Aliases-and-Glossary.md +129 -0
- package/dist/tutorials/Notifications.md +86 -0
- package/dist/tutorials/Other-Languages.md +110 -0
- package/dist/tutorials/PDF-Documents.md +100 -0
- package/dist/tutorials/Play-Images.md +135 -0
- package/dist/tutorials/Play-Narration.md +167 -0
- package/dist/tutorials/Play-Outputs.md +109 -0
- package/dist/tutorials/Play-Overview.md +216 -0
- package/dist/tutorials/Play-Title-and-Article.md +135 -0
- package/dist/tutorials/Play-Video-and-Style.md +143 -0
- package/dist/tutorials/Project-Page.md +228 -0
- package/dist/tutorials/Prompts.md +136 -0
- package/dist/tutorials/Providers-and-Keys.md +290 -0
- package/dist/tutorials/Publishing-to-YouTube.md +135 -0
- package/dist/tutorials/Recovery-and-Retries.md +141 -0
- package/dist/tutorials/Reviews-and-Checkpoints.md +143 -0
- package/dist/tutorials/Schedules.md +154 -0
- package/dist/tutorials/Settings-Reference.md +258 -0
- package/dist/tutorials/Shorts.md +116 -0
- package/dist/tutorials/Start-at-Login.md +126 -0
- package/dist/tutorials/Studio-Extension.md +146 -0
- package/dist/tutorials/Templates.md +146 -0
- package/dist/tutorials/Trash.md +87 -0
- package/dist/tutorials/Troubleshooting.md +149 -0
- package/dist/tutorials/Uninstalling-and-Moving.md +113 -0
- package/dist/tutorials/Updating-and-Patch-Notes.md +120 -0
- package/dist/tutorials/Video-Editing.md +135 -0
- package/dist/tutorials/Where-Your-Files-Live.md +149 -0
- package/dist/tutorials/YouTube-Description.md +132 -0
- package/dist/tutorials/Your-First-Short.md +125 -0
- package/dist/tutorials/_Footer.md +1 -0
- package/dist/tutorials/_Sidebar.md +69 -0
- package/dist/updater/service.js +66 -21
- package/dist/web/assets/index-DBLSfqA-.css +1 -0
- package/dist/web/assets/index-DFdIhmm0.js +307 -0
- package/dist/web/assets/jetbrains-mono-cyrillic-400-normal-BEIGL1Tu.woff2 +0 -0
- package/dist/web/assets/jetbrains-mono-cyrillic-400-normal-ugxPyKxw.woff +0 -0
- package/dist/web/assets/jetbrains-mono-greek-400-normal-B9oWc5Lo.woff +0 -0
- package/dist/web/assets/jetbrains-mono-greek-400-normal-C190GLew.woff2 +0 -0
- package/dist/web/assets/jetbrains-mono-latin-400-normal-6-qcROiO.woff +0 -0
- package/dist/web/assets/jetbrains-mono-latin-400-normal-V6pRDFza.woff2 +0 -0
- package/dist/web/assets/jetbrains-mono-latin-ext-400-normal-Bc8Ftmh3.woff2 +0 -0
- package/dist/web/assets/jetbrains-mono-latin-ext-400-normal-fXTG6kC5.woff +0 -0
- package/dist/web/assets/jetbrains-mono-vietnamese-400-normal-CqNFfHCs.woff +0 -0
- package/dist/web/assets/{pdf-BLF2i8c2.js → pdf-B9OY1ioX.js} +1 -1
- package/dist/web/index.html +2 -2
- package/package.json +5 -5
- package/dist/edge/docker-launch.js +0 -81
- package/dist/edge/docker-projects/activation.js +0 -35
- package/dist/edge/docker-projects/claims.js +0 -85
- package/dist/edge/docker-projects/committed.js +0 -40
- package/dist/edge/docker-projects/engine.js +0 -461
- package/dist/edge/docker-projects/install.js +0 -254
- package/dist/edge/docker-projects/prune.js +0 -66
- package/dist/edge/docker-projects/recover.js +0 -100
- package/dist/edge/docker-projects/state.js +0 -225
- package/dist/web/assets/index-CktpxlJd.js +0 -171
- package/dist/web/assets/index-DB-vOnGU.css +0 -1
- package/scripts/docker-run.sh +0 -3
- /package/dist/edge/{docker-projects → docker-install}/tree.js +0 -0
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { planAccountOf } from "../../kernel/ports/plan-limits.js";
|
|
2
|
+
import { resolveChannelId, scheduleChannel } from "../channels/repo.js";
|
|
3
|
+
import { priceCall } from "./pricing.js";
|
|
4
|
+
// Prices each call as it lands and keeps it with the rates it used; a CLI's plan windows go
|
|
5
|
+
// beside it. Separate from the anonymous telemetry log: this is per project, never leaves the
|
|
6
|
+
// machine, and is kept whether or not the usage-stats notice was dismissed.
|
|
7
|
+
export function createUsageMeter(deps) {
|
|
8
|
+
return {
|
|
9
|
+
record: (call) => {
|
|
10
|
+
const priced = priceCall(call, deps.catalogue());
|
|
11
|
+
const at = deps.clock.now().toISOString();
|
|
12
|
+
deps.db
|
|
13
|
+
.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
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`)
|
|
15
|
+
.run(deps.ids.next(), 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);
|
|
16
|
+
const account = planAccountOf(call.provider);
|
|
17
|
+
if (account !== undefined && call.limits !== undefined)
|
|
18
|
+
deps.db
|
|
19
|
+
.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);
|
|
21
|
+
},
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
// The same pricing for a call made outside a project (`kernel/runner/standalone.ts`), kept
|
|
25
|
+
// against its schedule or channel with the channel it counts under on Home.
|
|
26
|
+
export function createStandaloneMeter(deps) {
|
|
27
|
+
return {
|
|
28
|
+
record: (call) => {
|
|
29
|
+
const priced = priceCall(call, deps.catalogue());
|
|
30
|
+
const account = planAccountOf(call.provider);
|
|
31
|
+
const reading = account !== undefined && call.limits !== undefined
|
|
32
|
+
? { account, json: JSON.stringify(call.limits) }
|
|
33
|
+
: undefined;
|
|
34
|
+
deps.db
|
|
35
|
+
.prepare(`INSERT INTO standalone_usage (id, owner_kind, owner_id, channel_id, purpose, 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, account, reading_json, created_at)
|
|
36
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`)
|
|
37
|
+
.run(deps.ids.next(), call.owner.kind, call.owner.id, channelOf(deps.db, call.owner), call.purpose, 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), reading?.account ?? null, reading?.json ?? null, deps.clock.now().toISOString());
|
|
38
|
+
},
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
// A schedule counts under its template's channel; one whose template is gone, like a channel
|
|
42
|
+
// this install doesn't have, under the default channel.
|
|
43
|
+
function channelOf(db, owner) {
|
|
44
|
+
return owner.kind === "schedule"
|
|
45
|
+
? resolveChannelId(db, scheduleChannel(db, owner.id)?.id)
|
|
46
|
+
: resolveChannelId(db, owner.id);
|
|
47
|
+
}
|
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { stageKinds } from "../../kernel/pipeline.js";
|
|
3
|
+
import { planAccountNames, planAccountOf, planAccounts, } from "../../kernel/ports/plan-limits.js";
|
|
4
|
+
const usageRow = z.object({
|
|
5
|
+
stage: z.enum(stageKinds),
|
|
6
|
+
kind: z.enum(["llm", "tts", "image", "video"]),
|
|
7
|
+
provider: z.string(),
|
|
8
|
+
model: z.string(),
|
|
9
|
+
tokens_in: z.number().nullable(),
|
|
10
|
+
tokens_out: z.number().nullable(),
|
|
11
|
+
tokens_cached: z.number().nullable(),
|
|
12
|
+
characters: z.number().nullable(),
|
|
13
|
+
images: z.number().nullable(),
|
|
14
|
+
seconds: z.number().nullable(),
|
|
15
|
+
on_plan: z.number(),
|
|
16
|
+
cost: z.number().nullable(),
|
|
17
|
+
api_model: z.string().nullable(),
|
|
18
|
+
api_cost: z.number().nullable(),
|
|
19
|
+
price_json: z.string().nullable(),
|
|
20
|
+
});
|
|
21
|
+
const windowSchema = z.object({
|
|
22
|
+
kind: z.enum(["five_hour", "weekly", "other"]),
|
|
23
|
+
usedPercent: z.number(),
|
|
24
|
+
resetsAt: z.string().nullable(),
|
|
25
|
+
});
|
|
26
|
+
const readingSchema = z.object({
|
|
27
|
+
before: z.array(windowSchema).optional(),
|
|
28
|
+
after: z.array(windowSchema).optional(),
|
|
29
|
+
});
|
|
30
|
+
const emptyTotals = {
|
|
31
|
+
tokensIn: 0,
|
|
32
|
+
tokensOut: 0,
|
|
33
|
+
cachedTokens: 0,
|
|
34
|
+
characters: 0,
|
|
35
|
+
images: 0,
|
|
36
|
+
seconds: 0,
|
|
37
|
+
};
|
|
38
|
+
const emptyLine = {
|
|
39
|
+
calls: 0,
|
|
40
|
+
cost: 0,
|
|
41
|
+
unpriced: 0,
|
|
42
|
+
apiEquivalent: null,
|
|
43
|
+
apiUnpriced: 0,
|
|
44
|
+
};
|
|
45
|
+
function add(into, row) {
|
|
46
|
+
const onPlan = row.on_plan === 1;
|
|
47
|
+
return {
|
|
48
|
+
...into,
|
|
49
|
+
calls: into.calls + 1,
|
|
50
|
+
cost: into.cost + (row.cost ?? 0),
|
|
51
|
+
unpriced: into.unpriced + (row.cost === null ? 1 : 0),
|
|
52
|
+
apiEquivalent: onPlan ? (into.apiEquivalent ?? 0) + (row.api_cost ?? 0) : into.apiEquivalent,
|
|
53
|
+
apiUnpriced: into.apiUnpriced + (onPlan && row.api_cost === null ? 1 : 0),
|
|
54
|
+
tokensIn: into.tokensIn + (row.tokens_in ?? 0),
|
|
55
|
+
tokensOut: into.tokensOut + (row.tokens_out ?? 0),
|
|
56
|
+
cachedTokens: into.cachedTokens + (row.tokens_cached ?? 0),
|
|
57
|
+
characters: into.characters + (row.characters ?? 0),
|
|
58
|
+
images: into.images + (row.images ?? 0),
|
|
59
|
+
seconds: into.seconds + (row.seconds ?? 0),
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
export function runCostOf(db, projectId) {
|
|
63
|
+
const rows = db
|
|
64
|
+
.prepare("SELECT stage, kind, provider, model, tokens_in, tokens_out, tokens_cached, characters, images, seconds, on_plan, cost, api_model, api_cost, price_json FROM provider_usage WHERE project_id = ? ORDER BY created_at, rowid")
|
|
65
|
+
.all(projectId)
|
|
66
|
+
.map((row) => usageRow.parse(row));
|
|
67
|
+
const walls = stageWalls(db, projectId);
|
|
68
|
+
const stages = new Map();
|
|
69
|
+
const models = new Map();
|
|
70
|
+
let total = { ...emptyLine, ...emptyTotals };
|
|
71
|
+
let catalogueDate = null;
|
|
72
|
+
for (const row of rows) {
|
|
73
|
+
total = add(total, row);
|
|
74
|
+
stages.set(row.stage, add(stages.get(row.stage) ?? {
|
|
75
|
+
stage: row.stage,
|
|
76
|
+
wallMs: walls.get(row.stage) ?? null,
|
|
77
|
+
...emptyLine,
|
|
78
|
+
...emptyTotals,
|
|
79
|
+
}, row));
|
|
80
|
+
const key = JSON.stringify([row.provider, row.model, row.kind]);
|
|
81
|
+
models.set(key, add(models.get(key) ?? {
|
|
82
|
+
provider: row.provider,
|
|
83
|
+
model: row.model,
|
|
84
|
+
kind: row.kind,
|
|
85
|
+
onPlan: row.on_plan === 1,
|
|
86
|
+
apiModel: row.api_model,
|
|
87
|
+
...emptyLine,
|
|
88
|
+
...emptyTotals,
|
|
89
|
+
}, row));
|
|
90
|
+
catalogueDate = catalogueOf(row.price_json) ?? catalogueDate;
|
|
91
|
+
}
|
|
92
|
+
// A stage that ran with no provider call (a render, a document) still has its time.
|
|
93
|
+
for (const [stage, wallMs] of walls)
|
|
94
|
+
if (!stages.has(stage))
|
|
95
|
+
stages.set(stage, { stage, wallMs, ...emptyLine, ...emptyTotals });
|
|
96
|
+
const plans = planUses(db, projectId, rows);
|
|
97
|
+
return {
|
|
98
|
+
currency: "USD",
|
|
99
|
+
...total,
|
|
100
|
+
totals: {
|
|
101
|
+
tokensIn: total.tokensIn,
|
|
102
|
+
tokensOut: total.tokensOut,
|
|
103
|
+
cachedTokens: total.cachedTokens,
|
|
104
|
+
characters: total.characters,
|
|
105
|
+
images: total.images,
|
|
106
|
+
seconds: total.seconds,
|
|
107
|
+
wallMs: [...walls.values()].reduce((sum, ms) => sum + ms, 0),
|
|
108
|
+
},
|
|
109
|
+
byStage: stageKinds.flatMap((kind) => {
|
|
110
|
+
const found = stages.get(kind);
|
|
111
|
+
return found === undefined ? [] : [found];
|
|
112
|
+
}),
|
|
113
|
+
byModel: [...models.values()].toSorted((left, right) => right.cost - left.cost ||
|
|
114
|
+
(right.apiEquivalent ?? 0) - (left.apiEquivalent ?? 0) ||
|
|
115
|
+
left.provider.localeCompare(right.provider) ||
|
|
116
|
+
left.model.localeCompare(right.model)),
|
|
117
|
+
plans,
|
|
118
|
+
waits: limitWaitsOf(db, projectId),
|
|
119
|
+
catalogueDate,
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
function catalogueOf(json) {
|
|
123
|
+
if (json === null)
|
|
124
|
+
return null;
|
|
125
|
+
const parsed = z.object({ catalogue: z.string() }).safeParse(JSON.parse(json));
|
|
126
|
+
return parsed.success ? parsed.data.catalogue : null;
|
|
127
|
+
}
|
|
128
|
+
function stageWalls(db, projectId) {
|
|
129
|
+
const walls = new Map();
|
|
130
|
+
for (const raw of db
|
|
131
|
+
.prepare("SELECT kind, started_at, finished_at FROM stages WHERE project_id = ?")
|
|
132
|
+
.all(projectId)) {
|
|
133
|
+
const row = z
|
|
134
|
+
.object({
|
|
135
|
+
kind: z.enum(stageKinds),
|
|
136
|
+
started_at: z.string().nullable(),
|
|
137
|
+
finished_at: z.string().nullable(),
|
|
138
|
+
})
|
|
139
|
+
.parse(raw);
|
|
140
|
+
if (row.started_at === null || row.finished_at === null)
|
|
141
|
+
continue;
|
|
142
|
+
const ms = Date.parse(row.finished_at) - Date.parse(row.started_at);
|
|
143
|
+
if (Number.isFinite(ms) && ms >= 0)
|
|
144
|
+
walls.set(row.kind, ms);
|
|
145
|
+
}
|
|
146
|
+
return walls;
|
|
147
|
+
}
|
|
148
|
+
// Two readings belong to the same window when their resets agree to within a few minutes;
|
|
149
|
+
// a reset moving on means the window rolled over between them.
|
|
150
|
+
const sameWindowMs = 5 * 60_000;
|
|
151
|
+
export function windowUse(readings) {
|
|
152
|
+
const byKind = new Map();
|
|
153
|
+
for (const reading of readings)
|
|
154
|
+
for (const window of reading)
|
|
155
|
+
byKind.set(window.kind, [...(byKind.get(window.kind) ?? []), window]);
|
|
156
|
+
const uses = [];
|
|
157
|
+
for (const [kind, seen] of byKind) {
|
|
158
|
+
let used = 0;
|
|
159
|
+
for (let at = 1; at < seen.length; at += 1) {
|
|
160
|
+
const previous = seen[at - 1];
|
|
161
|
+
const current = seen[at];
|
|
162
|
+
if (previous === undefined || current === undefined)
|
|
163
|
+
continue;
|
|
164
|
+
const same = previous.resetsAt === current.resetsAt ||
|
|
165
|
+
(previous.resetsAt !== null &&
|
|
166
|
+
current.resetsAt !== null &&
|
|
167
|
+
Math.abs(Date.parse(previous.resetsAt) - Date.parse(current.resetsAt)) < sameWindowMs);
|
|
168
|
+
used += same ? Math.max(0, current.usedPercent - previous.usedPercent) : current.usedPercent;
|
|
169
|
+
}
|
|
170
|
+
const last = seen.at(-1);
|
|
171
|
+
if (last !== undefined)
|
|
172
|
+
uses.push({ kind, usedPercent: used, nowPercent: last.usedPercent, resetsAt: last.resetsAt });
|
|
173
|
+
}
|
|
174
|
+
const order = ["five_hour", "weekly", "other"];
|
|
175
|
+
return uses.toSorted((left, right) => order.indexOf(left.kind) - order.indexOf(right.kind));
|
|
176
|
+
}
|
|
177
|
+
function planUses(db, projectId, rows) {
|
|
178
|
+
const readings = db
|
|
179
|
+
.prepare("SELECT account, reading_json FROM plan_limit_readings WHERE project_id = ? ORDER BY created_at, rowid")
|
|
180
|
+
.all(projectId)
|
|
181
|
+
.map((row) => z.object({ account: z.string(), reading_json: z.string() }).parse(row));
|
|
182
|
+
return planAccounts.flatMap((account) => {
|
|
183
|
+
const calls = rows.filter((row) => planAccountOf(row.provider) === account).length;
|
|
184
|
+
if (calls === 0)
|
|
185
|
+
return [];
|
|
186
|
+
const sequence = [];
|
|
187
|
+
for (const row of readings) {
|
|
188
|
+
if (row.account !== account)
|
|
189
|
+
continue;
|
|
190
|
+
const parsed = readingSchema.safeParse(JSON.parse(row.reading_json));
|
|
191
|
+
if (!parsed.success)
|
|
192
|
+
continue;
|
|
193
|
+
if (parsed.data.before?.length)
|
|
194
|
+
sequence.push(parsed.data.before);
|
|
195
|
+
if (parsed.data.after?.length)
|
|
196
|
+
sequence.push(parsed.data.after);
|
|
197
|
+
}
|
|
198
|
+
return [
|
|
199
|
+
{
|
|
200
|
+
account,
|
|
201
|
+
name: planAccountNames[account],
|
|
202
|
+
calls,
|
|
203
|
+
reported: sequence.length > 0,
|
|
204
|
+
windows: windowUse(sequence),
|
|
205
|
+
},
|
|
206
|
+
];
|
|
207
|
+
});
|
|
208
|
+
}
|
|
209
|
+
export function limitWaitsOf(db, projectId) {
|
|
210
|
+
return limitWaitRows(db, projectId).map((row) => row.wait);
|
|
211
|
+
}
|
|
212
|
+
// Every project's waits at once, for the lists that show "Waiting for Codex limits" on a row
|
|
213
|
+
// (Projects, Home's Running now, the calendar) without asking once per project.
|
|
214
|
+
export function limitWaitsByProject(db) {
|
|
215
|
+
const found = new Map();
|
|
216
|
+
for (const row of limitWaitRows(db)) {
|
|
217
|
+
const list = found.get(row.projectId) ?? [];
|
|
218
|
+
list.push(row.wait);
|
|
219
|
+
found.set(row.projectId, list);
|
|
220
|
+
}
|
|
221
|
+
return found;
|
|
222
|
+
}
|
|
223
|
+
// A wait as a list row carries it: the plan's name and times, not its account key.
|
|
224
|
+
export function listingWait(wait) {
|
|
225
|
+
return { name: wait.name, stage: wait.stage, resetsAt: wait.resetsAt, retryAt: wait.retryAt };
|
|
226
|
+
}
|
|
227
|
+
function limitWaitRows(db, projectId) {
|
|
228
|
+
// Only a stage still running is waiting: a row left by a cancel or pause is not shown.
|
|
229
|
+
return db
|
|
230
|
+
.prepare(`SELECT w.project_id, w.account, w.stage, l.resets_at, l.retry_at FROM plan_limit_waiters w
|
|
231
|
+
JOIN plan_limit_waits l ON l.account = w.account
|
|
232
|
+
JOIN stages s ON s.project_id = w.project_id AND s.kind = w.stage AND s.state = 'running'
|
|
233
|
+
${projectId === undefined ? "" : "WHERE w.project_id = ?"} ORDER BY w.since`)
|
|
234
|
+
.all(...(projectId === undefined ? [] : [projectId]))
|
|
235
|
+
.map((raw) => {
|
|
236
|
+
const row = z
|
|
237
|
+
.object({
|
|
238
|
+
project_id: z.string(),
|
|
239
|
+
account: z.enum(planAccounts),
|
|
240
|
+
stage: z.enum(stageKinds),
|
|
241
|
+
resets_at: z.string().nullable(),
|
|
242
|
+
retry_at: z.string(),
|
|
243
|
+
})
|
|
244
|
+
.parse(raw);
|
|
245
|
+
return {
|
|
246
|
+
projectId: row.project_id,
|
|
247
|
+
wait: {
|
|
248
|
+
account: row.account,
|
|
249
|
+
name: planAccountNames[row.account],
|
|
250
|
+
stage: row.stage,
|
|
251
|
+
resetsAt: row.resets_at,
|
|
252
|
+
retryAt: row.retry_at,
|
|
253
|
+
},
|
|
254
|
+
};
|
|
255
|
+
});
|
|
256
|
+
}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { isLocalCliProvider, systemVoiceProvider } from "../settings/model.js";
|
|
2
|
+
// Per million tokens; cached input at its own rate when the catalogue gives one.
|
|
3
|
+
export function tokenCost(rates, tokensIn, tokensOut, cachedTokens = 0) {
|
|
4
|
+
if (rates.inputPerMillionTokens === undefined || rates.outputPerMillionTokens === undefined)
|
|
5
|
+
return null;
|
|
6
|
+
const cached = Math.min(Math.max(0, cachedTokens), tokensIn);
|
|
7
|
+
return (((tokensIn - cached) * rates.inputPerMillionTokens +
|
|
8
|
+
cached * (rates.cachedInputPerMillionTokens ?? rates.inputPerMillionTokens) +
|
|
9
|
+
tokensOut * rates.outputPerMillionTokens) /
|
|
10
|
+
1_000_000);
|
|
11
|
+
}
|
|
12
|
+
// The API model a CLI's model is billed as when it is called through a key: Claude Code's
|
|
13
|
+
// models are Anthropic's, Codex's are OpenAI's and Gemini's are Google's, all listed in the
|
|
14
|
+
// catalogue under OpenRouter. A version the catalogue does not list has no API price.
|
|
15
|
+
export function apiEquivalentOf(provider, model, catalogue) {
|
|
16
|
+
const listed = (id) => catalogue.llm.find((entry) => entry.provider === "openrouter" && entry.id === id);
|
|
17
|
+
// Context sizes ("[1m]") and dated snapshots ("-20251001") price as the model itself.
|
|
18
|
+
const bare = model
|
|
19
|
+
.replace(/\[[^\]]*\]/g, "")
|
|
20
|
+
.replace(/-\d{8}$/, "")
|
|
21
|
+
.trim();
|
|
22
|
+
if (provider === "claude-code") {
|
|
23
|
+
const family = /^(?:claude-)?(opus|sonnet|haiku|fable)(?:-(\d+)(?:-(\d{1,2}))?)?$/i.exec(bare);
|
|
24
|
+
if (family === null)
|
|
25
|
+
return undefined;
|
|
26
|
+
const name = (family[1] ?? "").toLowerCase();
|
|
27
|
+
if (family[2] !== undefined)
|
|
28
|
+
return listed(`anthropic/claude-${name}-${family[2]}${family[3] ? `.${family[3]}` : ""}`);
|
|
29
|
+
// An alias ("sonnet") is the newest of that family the catalogue lists.
|
|
30
|
+
return catalogue.llm
|
|
31
|
+
.filter((entry) => entry.provider === "openrouter" && entry.id.startsWith(`anthropic/claude-${name}-`))
|
|
32
|
+
.toSorted((left, right) => right.id.localeCompare(left.id, "en", { numeric: true }))[0];
|
|
33
|
+
}
|
|
34
|
+
if (bare === "" || bare === "codex-imagegen")
|
|
35
|
+
return undefined;
|
|
36
|
+
if (provider === "codex" || provider === "codex-image")
|
|
37
|
+
return listed(`openai/${bare}`);
|
|
38
|
+
if (provider === "gemini")
|
|
39
|
+
return listed(`google/${bare}`);
|
|
40
|
+
return undefined;
|
|
41
|
+
}
|
|
42
|
+
export function priceCall(call, catalogue) {
|
|
43
|
+
const date = catalogue.updatedAt;
|
|
44
|
+
if (isLocalCliProvider(call.provider)) {
|
|
45
|
+
// Only text is priced through the API: what a Codex image would cost there depends on
|
|
46
|
+
// how many drafts the agent drew, which the CLI does not say.
|
|
47
|
+
const api = call.kind === "llm" ? apiEquivalentOf(call.provider, call.model, catalogue) : undefined;
|
|
48
|
+
const apiCost = api === undefined || call.tokensIn === undefined || call.tokensOut === undefined
|
|
49
|
+
? null
|
|
50
|
+
: tokenCost(api.pricing, call.tokensIn, call.tokensOut, call.cachedTokens);
|
|
51
|
+
return {
|
|
52
|
+
onPlan: true,
|
|
53
|
+
cost: 0,
|
|
54
|
+
apiModel: api?.id ?? null,
|
|
55
|
+
apiCost,
|
|
56
|
+
price: api === undefined ? null : { catalogue: date, model: api.id, ...api.pricing },
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
// The computer's own voice costs nothing.
|
|
60
|
+
if (call.provider === systemVoiceProvider)
|
|
61
|
+
return { onPlan: false, cost: 0, apiModel: null, apiCost: null, price: null };
|
|
62
|
+
const family = call.kind === "video" ? "image" : call.kind;
|
|
63
|
+
const entry = catalogue[family].find((model) => model.provider === call.provider && model.id === call.model);
|
|
64
|
+
const rates = entry?.pricing;
|
|
65
|
+
let cost = null;
|
|
66
|
+
if (rates !== undefined)
|
|
67
|
+
switch (call.kind) {
|
|
68
|
+
case "llm":
|
|
69
|
+
cost =
|
|
70
|
+
call.tokensIn === undefined || call.tokensOut === undefined
|
|
71
|
+
? null
|
|
72
|
+
: tokenCost(rates, call.tokensIn, call.tokensOut, call.cachedTokens);
|
|
73
|
+
break;
|
|
74
|
+
case "tts":
|
|
75
|
+
// ceiling: a per-minute voice is left unpriced rather than guessed from characters;
|
|
76
|
+
// the narration's measured length is known only after the stage writes the file.
|
|
77
|
+
cost =
|
|
78
|
+
rates.perMillionCharacters === undefined || call.characters === undefined
|
|
79
|
+
? null
|
|
80
|
+
: (call.characters / 1_000_000) * rates.perMillionCharacters;
|
|
81
|
+
break;
|
|
82
|
+
case "image":
|
|
83
|
+
case "video":
|
|
84
|
+
// An image-to-video model's perImage is the price of one clip.
|
|
85
|
+
cost = rates.perImage === undefined ? null : rates.perImage * (call.images ?? 1);
|
|
86
|
+
break;
|
|
87
|
+
}
|
|
88
|
+
return {
|
|
89
|
+
onPlan: false,
|
|
90
|
+
cost,
|
|
91
|
+
apiModel: null,
|
|
92
|
+
apiCost: null,
|
|
93
|
+
price: rates === undefined ? null : { catalogue: date, model: call.model, ...rates },
|
|
94
|
+
};
|
|
95
|
+
}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { planAccountNames, planAccounts, } from "../../kernel/ports/plan-limits.js";
|
|
3
|
+
import { defaultChannelId } from "../channels/model.js";
|
|
4
|
+
import { projectChannels } from "../channels/repo.js";
|
|
5
|
+
const usageRow = z.object({
|
|
6
|
+
project_id: z.string(),
|
|
7
|
+
on_plan: z.number(),
|
|
8
|
+
cost: z.number().nullable(),
|
|
9
|
+
api_cost: z.number().nullable(),
|
|
10
|
+
});
|
|
11
|
+
const standaloneRow = z.object({
|
|
12
|
+
channel_id: z.string(),
|
|
13
|
+
on_plan: z.number(),
|
|
14
|
+
cost: z.number().nullable(),
|
|
15
|
+
api_cost: z.number().nullable(),
|
|
16
|
+
});
|
|
17
|
+
const windowSchema = z.object({
|
|
18
|
+
kind: z.enum(["five_hour", "weekly", "other"]),
|
|
19
|
+
usedPercent: z.number(),
|
|
20
|
+
resetsAt: z.string().nullable(),
|
|
21
|
+
});
|
|
22
|
+
const readingSchema = z.object({
|
|
23
|
+
before: z.array(windowSchema).optional(),
|
|
24
|
+
after: z.array(windowSchema).optional(),
|
|
25
|
+
});
|
|
26
|
+
// `channelId` narrows the counts to one channel: its projects, and the calls made for its
|
|
27
|
+
// schedules and itself. Plan standings are the account's whole allowance, which every channel
|
|
28
|
+
// shares, so they are never narrowed.
|
|
29
|
+
export function weekSummary(db, since, channelId) {
|
|
30
|
+
const known = channelId === undefined ? undefined : knownChannels(db);
|
|
31
|
+
const channels = channelId === undefined ? undefined : projectChannels(db);
|
|
32
|
+
const projectInScope = (projectId) => channels === undefined || (channels.get(projectId) ?? defaultChannelId) === channelId;
|
|
33
|
+
let calls = 0;
|
|
34
|
+
let cost = 0;
|
|
35
|
+
let unpriced = 0;
|
|
36
|
+
let apiEquivalent = null;
|
|
37
|
+
const count = (row) => {
|
|
38
|
+
calls += 1;
|
|
39
|
+
cost += row.cost ?? 0;
|
|
40
|
+
if (row.cost === null)
|
|
41
|
+
unpriced += 1;
|
|
42
|
+
if (row.on_plan === 1)
|
|
43
|
+
apiEquivalent = (apiEquivalent ?? 0) + (row.api_cost ?? 0);
|
|
44
|
+
};
|
|
45
|
+
for (const raw of db
|
|
46
|
+
.prepare("SELECT project_id, on_plan, cost, api_cost FROM provider_usage WHERE created_at >= ?")
|
|
47
|
+
.all(since)) {
|
|
48
|
+
const row = usageRow.parse(raw);
|
|
49
|
+
if (projectInScope(row.project_id))
|
|
50
|
+
count(row);
|
|
51
|
+
}
|
|
52
|
+
for (const raw of db
|
|
53
|
+
.prepare("SELECT channel_id, on_plan, cost, api_cost FROM standalone_usage WHERE created_at >= ?")
|
|
54
|
+
.all(since)) {
|
|
55
|
+
const row = standaloneRow.parse(raw);
|
|
56
|
+
// A channel deleted since counts under the default channel, as its projects do.
|
|
57
|
+
const channel = known === undefined || known.has(row.channel_id) ? row.channel_id : defaultChannelId;
|
|
58
|
+
if (channelId === undefined || channel === channelId)
|
|
59
|
+
count(row);
|
|
60
|
+
}
|
|
61
|
+
// A video is made when its file lands; a remade video counts again, as it was made again.
|
|
62
|
+
const videos = db
|
|
63
|
+
.prepare("SELECT project_id FROM outputs WHERE role = 'video' AND created_at >= ?")
|
|
64
|
+
.all(since)
|
|
65
|
+
.filter((row) => projectInScope(String(row.project_id))).length;
|
|
66
|
+
return { since, videos, calls, cost, unpriced, apiEquivalent, plans: planStandings(db) };
|
|
67
|
+
}
|
|
68
|
+
function knownChannels(db) {
|
|
69
|
+
return new Set(db
|
|
70
|
+
.prepare("SELECT id FROM channels")
|
|
71
|
+
.all()
|
|
72
|
+
.map((row) => String(row.id)));
|
|
73
|
+
}
|
|
74
|
+
function planStandings(db) {
|
|
75
|
+
return planAccounts.flatMap((account) => {
|
|
76
|
+
const rows = db
|
|
77
|
+
.prepare(`SELECT reading_json, created_at FROM (
|
|
78
|
+
SELECT reading_json, created_at, rowid AS n, 0 AS source FROM plan_limit_readings WHERE account = ?
|
|
79
|
+
UNION ALL
|
|
80
|
+
SELECT reading_json, created_at, rowid AS n, 1 AS source FROM standalone_usage WHERE account = ? AND reading_json IS NOT NULL
|
|
81
|
+
) ORDER BY created_at DESC, source DESC, n DESC LIMIT 20`)
|
|
82
|
+
.all(account, account);
|
|
83
|
+
for (const raw of rows) {
|
|
84
|
+
const row = z.object({ reading_json: z.string(), created_at: z.string() }).parse(raw);
|
|
85
|
+
const parsed = readingSchema.safeParse(JSON.parse(row.reading_json));
|
|
86
|
+
if (!parsed.success)
|
|
87
|
+
continue;
|
|
88
|
+
const windows = parsed.data.after?.length
|
|
89
|
+
? parsed.data.after
|
|
90
|
+
: (parsed.data.before ?? []);
|
|
91
|
+
if (windows.length === 0)
|
|
92
|
+
continue;
|
|
93
|
+
const weekly = windows.find((window) => window.kind === "weekly");
|
|
94
|
+
const fiveHour = windows.find((window) => window.kind === "five_hour");
|
|
95
|
+
return [
|
|
96
|
+
{
|
|
97
|
+
account,
|
|
98
|
+
name: planAccountNames[account],
|
|
99
|
+
weeklyPercent: weekly?.usedPercent ?? null,
|
|
100
|
+
fiveHourPercent: fiveHour?.usedPercent ?? null,
|
|
101
|
+
weeklyResetsAt: weekly?.resetsAt ?? null,
|
|
102
|
+
readAt: row.created_at,
|
|
103
|
+
},
|
|
104
|
+
];
|
|
105
|
+
}
|
|
106
|
+
return [];
|
|
107
|
+
});
|
|
108
|
+
}
|