@vfrey/vfrey 0.0.1 → 1.0.0-beta.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/LICENSE +33 -0
- package/README.md +78 -3
- package/accounts.example.json +61 -0
- package/accounts.first-run.json +41 -0
- package/bin/vfrey.mjs +36 -0
- package/integration-tools/README.md +43 -0
- package/integration-tools/image/pillow.md +32 -0
- package/integration-tools/image/playwright.md +33 -0
- package/integration-tools/image/resvg.md +36 -0
- package/integration-tools/image/satori.md +36 -0
- package/integration-tools/image/sharp.md +38 -0
- package/integration-tools/video/blender.md +34 -0
- package/integration-tools/video/ffmpeg.md +41 -0
- package/integration-tools/video/manim.md +37 -0
- package/integration-tools/video/motion-canvas.md +37 -0
- package/integration-tools/video/remotion.md +50 -0
- package/integration-tools/video/revideo.md +32 -0
- package/package.json +74 -6
- package/skills/vfrey-orchestrator/SKILL.md +15 -0
- package/skills/vfrey-orchestrator/channel-video/SKILL.md +41 -0
- package/skills/vfrey-product/SKILL.md +63 -0
- package/skills/vfrey-product/examples/concurrency-vs-parallelism/framey/cook-one.svg +14 -0
- package/skills/vfrey-product/examples/concurrency-vs-parallelism/framey/cook-two.svg +22 -0
- package/skills/vfrey-product/examples/concurrency-vs-parallelism/framey/index.html +70 -0
- package/skills/vfrey-product/examples/concurrency-vs-parallelism/remotion/cook-one.svg +14 -0
- package/skills/vfrey-product/examples/concurrency-vs-parallelism/remotion/cook-two.svg +22 -0
- package/skills/vfrey-product/examples/concurrency-vs-parallelism/remotion/index.tsx +49 -0
- package/skills/vfrey-product/examples/concurrency-vs-parallelism/script.json +15 -0
- package/skills/vfrey-product/examples/octopus-facts/index.html +60 -0
- package/skills/vfrey-product/examples/octopus-facts/octopus.svg +8 -0
- package/skills/vfrey-product/examples/octopus-facts/script.json +14 -0
- package/skills/vfrey-product/push/SKILL.md +15 -0
- package/skills/vfrey-product/scenes/NOTICE +5 -0
- package/skills/vfrey-product/scenes/blocks.md +57 -0
- package/skills/vfrey-product/scenes/craft.md +118 -0
- package/skills/vfrey-product/scenes/motion.md +88 -0
- package/src/analytics/dashboard.ts +109 -0
- package/src/analytics/index.ts +260 -0
- package/src/api/client.ts +282 -0
- package/src/api/commands.ts +330 -0
- package/src/api/config.ts +34 -0
- package/src/api/creators.ts +463 -0
- package/src/api/credentials.ts +152 -0
- package/src/api/mail.ts +333 -0
- package/src/api/topics.ts +207 -0
- package/src/api/types.ts +205 -0
- package/src/cli.ts +467 -0
- package/src/cloud/executor.ts +281 -0
- package/src/cloud/job.ts +106 -0
- package/src/cloud/render-job.ts +187 -0
- package/src/config.ts +309 -0
- package/src/dashboard-link.ts +180 -0
- package/src/dashboard-start.ts +103 -0
- package/src/desk/channel.ts +64 -0
- package/src/desk/cli.ts +216 -0
- package/src/desk/creators-route.ts +129 -0
- package/src/desk/fonts/JetBrainsMono-OFL.txt +93 -0
- package/src/desk/fonts/google-sans-flex-latin.woff2 +0 -0
- package/src/desk/fonts/jetbrains-mono-medium.woff2 +0 -0
- package/src/desk/http.ts +460 -0
- package/src/desk/mail-route.ts +103 -0
- package/src/desk/overlay.ts +112 -0
- package/src/desk/preview.ts +422 -0
- package/src/desk/screens.ts +149 -0
- package/src/desk/secrets.ts +143 -0
- package/src/desk/series.ts +127 -0
- package/src/desk/state.ts +77 -0
- package/src/desk/studio-route.ts +119 -0
- package/src/desk/tick-runner.ts +66 -0
- package/src/desk/tick.ts +56 -0
- package/src/desk/topics-route.ts +41 -0
- package/src/desk/ui/app.css +505 -0
- package/src/desk/ui/app.js +185 -0
- package/src/desk/ui/apple-icon.png +0 -0
- package/src/desk/ui/components.js +79 -0
- package/src/desk/ui/core.js +156 -0
- package/src/desk/ui/favicon.ico +0 -0
- package/src/desk/ui/icon.svg +1 -0
- package/src/desk/ui/icons.js +49 -0
- package/src/desk/ui/index.html +41 -0
- package/src/desk/ui/metrics-math.d.ts +22 -0
- package/src/desk/ui/metrics-math.js +135 -0
- package/src/desk/ui/theme.js +5 -0
- package/src/desk/ui/views-creators.js +209 -0
- package/src/desk/ui/views-keys.js +114 -0
- package/src/desk/ui/views-mail.js +188 -0
- package/src/desk/ui/views-misc.js +123 -0
- package/src/desk/ui/views-review.js +119 -0
- package/src/desk/ui/views-runs.js +350 -0
- package/src/desk/ui/views-screens.js +163 -0
- package/src/desk/ui/views-series.js +318 -0
- package/src/desk/ui/views-topics.js +87 -0
- package/src/desk/verbs.ts +361 -0
- package/src/doctor.ts +212 -0
- package/src/drain.ts +14 -0
- package/src/engage/index.ts +217 -0
- package/src/generate/index.ts +110 -0
- package/src/generate/script.ts +152 -0
- package/src/generate/title.ts +78 -0
- package/src/help.ts +233 -0
- package/src/home.ts +45 -0
- package/src/llm/client.ts +177 -0
- package/src/log.ts +7 -0
- package/src/oauth/tokens.ts +189 -0
- package/src/oauth/vault.ts +72 -0
- package/src/pipeline/review.ts +642 -0
- package/src/pipeline/run.ts +443 -0
- package/src/pipeline/series-guard.ts +26 -0
- package/src/pipeline/source-fetch.ts +69 -0
- package/src/pipeline/stages.ts +728 -0
- package/src/pipeline/topics-source.ts +90 -0
- package/src/product/commands.ts +296 -0
- package/src/product/config-file.ts +38 -0
- package/src/product/engine-choice.ts +89 -0
- package/src/product/engines.ts +103 -0
- package/src/product/insights.ts +50 -0
- package/src/product/keys.ts +67 -0
- package/src/product/paths.ts +18 -0
- package/src/product/probes.ts +88 -0
- package/src/product/push-flow.ts +90 -0
- package/src/product/render-video.ts +21 -0
- package/src/product/short.ts +62 -0
- package/src/product/update.ts +32 -0
- package/src/project/canonical.ts +14 -0
- package/src/project/commands.ts +177 -0
- package/src/project/dashboard-status.ts +28 -0
- package/src/project/discover.ts +19 -0
- package/src/project/index-file.ts +73 -0
- package/src/project/marker.ts +87 -0
- package/src/project/migrate.ts +201 -0
- package/src/project/names.ts +27 -0
- package/src/project/notice.ts +16 -0
- package/src/project/open.ts +88 -0
- package/src/project/scaffold.ts +112 -0
- package/src/project/templates.ts +42 -0
- package/src/project/videos.ts +64 -0
- package/src/publish/facebook.ts +91 -0
- package/src/publish/index.ts +12 -0
- package/src/publish/instagram.ts +97 -0
- package/src/publish/rate.ts +62 -0
- package/src/publish/session.ts +242 -0
- package/src/publish/tiktok.ts +202 -0
- package/src/publish/youtube.ts +177 -0
- package/src/queue/desk-ops.ts +1170 -0
- package/src/queue/seal.ts +3 -0
- package/src/queue/store.ts +1006 -0
- package/src/remotion/AiLabel.tsx +26 -0
- package/src/remotion/Root.tsx +73 -0
- package/src/remotion/defaults.ts +26 -0
- package/src/remotion/films/claude/ClaudeFilm.tsx +1014 -0
- package/src/remotion/films/engine/EngineMark.tsx +17 -0
- package/src/remotion/films/rea/ReaShort.tsx +552 -0
- package/src/remotion/films/rea/drawings.tsx +907 -0
- package/src/remotion/films/rea/timing.ts +144 -0
- package/src/remotion/index.ts +4 -0
- package/src/remotion/templates/EditorialSplit.tsx +39 -0
- package/src/remotion/templates/KineticType.tsx +39 -0
- package/src/remotion/templates/SignalBoard.tsx +50 -0
- package/src/remotion/timing.ts +22 -0
- package/src/render/chrome.ts +78 -0
- package/src/render/engine-mark.ts +29 -0
- package/src/render/framey-template.ts +130 -0
- package/src/render/html-engine.ts +149 -0
- package/src/render/node-crypto-stub.ts +11 -0
- package/src/render/probe.ts +29 -0
- package/src/render/props.ts +17 -0
- package/src/render/vertical-short.ts +35 -0
- package/src/render/webpack.ts +33 -0
- package/src/render/worker.ts +65 -0
- package/src/scene/assets/fonts/TitanOne-OFL.txt +94 -0
- package/src/scene/assets/fonts/titan-one.woff2 +0 -0
- package/src/scene/assets/gsap/LICENSE.txt +8 -0
- package/src/scene/assets/gsap/gsap.min.js +11 -0
- package/src/scene/audio.ts +54 -0
- package/src/scene/blocks/LICENSE +190 -0
- package/src/scene/blocks/NOTICE +5 -0
- package/src/scene/blocks/bar-chart-race/bar-chart-race.html +585 -0
- package/src/scene/blocks/beat-freeze-cut/beat-freeze-cut.html +874 -0
- package/src/scene/blocks/camcorder-hud/camcorder-hud.html +164 -0
- package/src/scene/blocks/camera-dolly-zoom/camera-dolly-zoom.html +355 -0
- package/src/scene/blocks/code-diff/code-diff.html +817 -0
- package/src/scene/blocks/code-snippet-apple-terminal-pro/code-snippet-apple-terminal-pro.html +248 -0
- package/src/scene/blocks/code-typing/code-typing.html +764 -0
- package/src/scene/blocks/data-chart/data-chart.html +452 -0
- package/src/scene/blocks/flowchart-vertical/flowchart-vertical.html +454 -0
- package/src/scene/blocks/hw-scribble-transition/hw-scribble-transition.html +153 -0
- package/src/scene/blocks/index.json +824 -0
- package/src/scene/blocks/lt-accent-underline/lt-accent-underline.html +107 -0
- package/src/scene/blocks/lt-bold-block/lt-bold-block.html +109 -0
- package/src/scene/blocks/lt-clean-bar/lt-clean-bar.html +112 -0
- package/src/scene/blocks/lt-dark-card/lt-dark-card.html +111 -0
- package/src/scene/blocks/lt-kicker-name/lt-kicker-name.html +114 -0
- package/src/scene/blocks/lt-mask-reveal/lt-mask-reveal.html +120 -0
- package/src/scene/blocks/lt-neon-border/lt-neon-border.html +391 -0
- package/src/scene/blocks/lt-stack-bars/lt-stack-bars.html +109 -0
- package/src/scene/blocks/macos-notification/macos-notification.html +194 -0
- package/src/scene/blocks/mk-background/mk-background.html +236 -0
- package/src/scene/blocks/mk-callout-highlight/mk-callout-highlight.html +183 -0
- package/src/scene/blocks/mk-clone-wall-transition/mk-clone-wall-transition.html +226 -0
- package/src/scene/blocks/mk-line-graph/mk-line-graph.html +293 -0
- package/src/scene/blocks/mk-progress-stat/mk-progress-stat.html +186 -0
- package/src/scene/blocks/mk-specs-list/mk-specs-list.html +223 -0
- package/src/scene/blocks/news-ticker/news-ticker.html +409 -0
- package/src/scene/blocks/organic-light-leak-overlay/organic-light-leak-overlay.html +126 -0
- package/src/scene/blocks/oscilloscope-trace/oscilloscope-trace.html +519 -0
- package/src/scene/blocks/snow/snow.html +205 -0
- package/src/scene/blocks/split-flap-board/split-flap-board.html +347 -0
- package/src/scene/blocks/spotify-card/spotify-card.html +294 -0
- package/src/scene/blocks/starfield/starfield.html +188 -0
- package/src/scene/blocks/thread-message-stack/thread-message-stack.html +239 -0
- package/src/scene/blocks/week-in-merges/week-in-merges.html +470 -0
- package/src/scene/blocks/x-post/x-post.html +437 -0
- package/src/scene/blocks/yt-comment-card/yt-comment-card.html +319 -0
- package/src/scene/blocks/yt-lcd-background/yt-lcd-background.html +307 -0
- package/src/scene/blocks/yt-prism-title/yt-prism-title.html +205 -0
- package/src/scene/blocks.ts +229 -0
- package/src/scene/command.ts +272 -0
- package/src/scene/figure.ts +181 -0
- package/src/scene/glyphs.ts +107 -0
- package/src/scene/init.ts +86 -0
- package/src/scene/inspect/css-tree.d.ts +6 -0
- package/src/scene/inspect/page-scripts.ts +272 -0
- package/src/scene/inspect/report.ts +405 -0
- package/src/scene/inspect/run.ts +200 -0
- package/src/scene/inspect/source.ts +236 -0
- package/src/scene/label.ts +131 -0
- package/src/scene/net-guard.ts +74 -0
- package/src/scene/render.ts +292 -0
- package/src/scene/runtime/browser.ts +199 -0
- package/src/scene/runtime/core.ts +181 -0
- package/src/scene/runtime/react.tsx +115 -0
- package/src/scene/runtime/transitions.ts +133 -0
- package/src/scene/sanitize.ts +181 -0
- package/src/scene/scene.ts +152 -0
- package/src/scene/studio-command.ts +48 -0
- package/src/scene/studio-skin.ts +59 -0
- package/src/scene/studio.ts +337 -0
- package/src/scene/supply.ts +111 -0
- package/src/scene/timing.ts +288 -0
- package/src/scene/transitions/LICENSE +190 -0
- package/src/scene/transitions/NOTICE +5 -0
- package/src/scene/transitions/SOURCE.json +24 -0
- package/src/scene/transitions/transitions.global.js +38 -0
- package/src/scene/transitions.ts +26 -0
- package/src/service/all-projects.ts +153 -0
- package/src/service/commands.ts +256 -0
- package/src/service/due.ts +82 -0
- package/src/service/lock.ts +101 -0
- package/src/service/log-file.ts +56 -0
- package/src/service/state.ts +41 -0
- package/src/service/units.ts +90 -0
- package/src/service/worker.ts +51 -0
- package/src/skill-agents.ts +74 -0
- package/src/skill-install.ts +334 -0
- package/src/sync-write.ts +21 -0
- package/src/tick-report.ts +205 -0
- package/src/tts.ts +124 -0
- package/vendor/@vfrey/contracts/dist/account.d.ts +97 -0
- package/vendor/@vfrey/contracts/dist/account.js +27 -0
- package/vendor/@vfrey/contracts/dist/checks.d.ts +8 -0
- package/vendor/@vfrey/contracts/dist/checks.js +1 -0
- package/vendor/@vfrey/contracts/dist/credentials/crypto.d.ts +4 -0
- package/vendor/@vfrey/contracts/dist/credentials/crypto.js +38 -0
- package/vendor/@vfrey/contracts/dist/domain/advice-persona.d.ts +10 -0
- package/vendor/@vfrey/contracts/dist/domain/advice-persona.js +24 -0
- package/vendor/@vfrey/contracts/dist/domain/backoff.d.ts +13 -0
- package/vendor/@vfrey/contracts/dist/domain/backoff.js +27 -0
- package/vendor/@vfrey/contracts/dist/domain/cadence.d.ts +29 -0
- package/vendor/@vfrey/contracts/dist/domain/cadence.js +95 -0
- package/vendor/@vfrey/contracts/dist/domain/creators.d.ts +146 -0
- package/vendor/@vfrey/contracts/dist/domain/creators.js +2 -0
- package/vendor/@vfrey/contracts/dist/domain/disclosure.d.ts +127 -0
- package/vendor/@vfrey/contracts/dist/domain/disclosure.js +250 -0
- package/vendor/@vfrey/contracts/dist/domain/errors.d.ts +34 -0
- package/vendor/@vfrey/contracts/dist/domain/errors.js +68 -0
- package/vendor/@vfrey/contracts/dist/domain/redact.d.ts +1 -0
- package/vendor/@vfrey/contracts/dist/domain/redact.js +21 -0
- package/vendor/@vfrey/contracts/dist/domain/revenue.d.ts +13 -0
- package/vendor/@vfrey/contracts/dist/domain/revenue.js +16 -0
- package/vendor/@vfrey/contracts/dist/domain/safety.d.ts +20 -0
- package/vendor/@vfrey/contracts/dist/domain/safety.js +61 -0
- package/vendor/@vfrey/contracts/dist/domain/similarity.d.ts +13 -0
- package/vendor/@vfrey/contracts/dist/domain/similarity.js +100 -0
- package/vendor/@vfrey/contracts/dist/domain/templates.d.ts +18 -0
- package/vendor/@vfrey/contracts/dist/domain/templates.js +67 -0
- package/vendor/@vfrey/contracts/dist/domain/tiktok-chunks.d.ts +19 -0
- package/vendor/@vfrey/contracts/dist/domain/tiktok-chunks.js +52 -0
- package/vendor/@vfrey/contracts/dist/domain/topics.d.ts +78 -0
- package/vendor/@vfrey/contracts/dist/domain/topics.js +4 -0
- package/vendor/@vfrey/contracts/dist/domain/types.d.ts +216 -0
- package/vendor/@vfrey/contracts/dist/domain/types.js +1 -0
- package/vendor/@vfrey/contracts/dist/domain/variation.d.ts +57 -0
- package/vendor/@vfrey/contracts/dist/domain/variation.js +278 -0
- package/vendor/@vfrey/contracts/dist/events.d.ts +20 -0
- package/vendor/@vfrey/contracts/dist/events.js +1 -0
- package/vendor/@vfrey/contracts/dist/http.d.ts +11 -0
- package/vendor/@vfrey/contracts/dist/http.js +37 -0
- package/vendor/@vfrey/contracts/dist/index.d.ts +22 -0
- package/vendor/@vfrey/contracts/dist/index.js +22 -0
- package/vendor/@vfrey/contracts/dist/log.d.ts +2 -0
- package/vendor/@vfrey/contracts/dist/log.js +10 -0
- package/vendor/@vfrey/contracts/dist/mail.d.ts +55 -0
- package/vendor/@vfrey/contracts/dist/mail.js +1 -0
- package/vendor/@vfrey/contracts/package.json +18 -0
- package/vendor/@vfrey/ingest/dist/index.d.ts +41 -0
- package/vendor/@vfrey/ingest/dist/index.js +221 -0
- package/vendor/@vfrey/ingest/dist/parse.d.ts +5 -0
- package/vendor/@vfrey/ingest/dist/parse.js +165 -0
- package/vendor/@vfrey/ingest/package.json +21 -0
- package/vendor/framey/LICENSE +190 -0
- package/vendor/framey/NOTICE +15 -0
- package/vendor/framey/SOURCE.json +24 -0
- package/vendor/framey/STUDIO-SOURCE.json +9 -0
- package/vendor/framey/bin/framey-localize-fonts.mjs +12 -0
- package/vendor/framey/bin/framey.mjs +16 -0
- package/vendor/framey/dist/add-VMJD5KBS.js +75 -0
- package/vendor/framey/dist/animatedGifPrep-QPKGJ6PM.js +43 -0
- package/vendor/framey/dist/api.d.ts +211 -0
- package/vendor/framey/dist/api.js +76 -0
- package/vendor/framey/dist/appHistory-WW56FZTT.js +31 -0
- package/vendor/framey/dist/asset-WQKGOELE.js +26 -0
- package/vendor/framey/dist/audio-RSJWVWRA.js +1936 -0
- package/vendor/framey/dist/batchRender-3HPICDCT.js +366 -0
- package/vendor/framey/dist/beats-HL2BDUZI.js +273 -0
- package/vendor/framey/dist/benchmark-RWCWHI5Z.js +230 -0
- package/vendor/framey/dist/browser-ECYET3Q2.js +206 -0
- package/vendor/framey/dist/bundleWithLocalizedFonts-ALYBQEQT.js +52 -0
- package/vendor/framey/dist/captureCompositionFrame-4DA6SEH2.js +94 -0
- package/vendor/framey/dist/catalog-5JVDEUWK.js +979 -0
- package/vendor/framey/dist/catch-up-NEBCL5NO.js +106 -0
- package/vendor/framey/dist/check-F5N544ML.js +111 -0
- package/vendor/framey/dist/checkBrowser-LZ66CMJG.js +110 -0
- package/vendor/framey/dist/chunk-2K7Z2HVP.js +372 -0
- package/vendor/framey/dist/chunk-2UZQHDM7.js +323 -0
- package/vendor/framey/dist/chunk-2X4PQEYM.js +34 -0
- package/vendor/framey/dist/chunk-3422KMTD.js +168 -0
- package/vendor/framey/dist/chunk-3FYD4YEK.js +72 -0
- package/vendor/framey/dist/chunk-3GXQPCJC.js +682 -0
- package/vendor/framey/dist/chunk-3LM6YPBR.js +179 -0
- package/vendor/framey/dist/chunk-442JNXRM.js +20 -0
- package/vendor/framey/dist/chunk-4BNN5FGX.js +182 -0
- package/vendor/framey/dist/chunk-4P7NX5BM.js +11532 -0
- package/vendor/framey/dist/chunk-4UMPIPCQ.js +88 -0
- package/vendor/framey/dist/chunk-4YYJOFKS.js +28 -0
- package/vendor/framey/dist/chunk-54HJ63Q2.js +29 -0
- package/vendor/framey/dist/chunk-5ETJX6WM.js +89 -0
- package/vendor/framey/dist/chunk-5SG54JRS.js +68 -0
- package/vendor/framey/dist/chunk-634HSJ3J.js +58 -0
- package/vendor/framey/dist/chunk-66LREXGS.js +48 -0
- package/vendor/framey/dist/chunk-6BEFEWFB.js +25 -0
- package/vendor/framey/dist/chunk-6BNTGNGD.js +286 -0
- package/vendor/framey/dist/chunk-6DDPOHMK.js +140 -0
- package/vendor/framey/dist/chunk-6GV5XSOR.js +214 -0
- package/vendor/framey/dist/chunk-6JR2XUEE.js +127 -0
- package/vendor/framey/dist/chunk-73QVATIM.js +362 -0
- package/vendor/framey/dist/chunk-7FMUTGOH.js +279 -0
- package/vendor/framey/dist/chunk-7JQFJ6JJ.js +291 -0
- package/vendor/framey/dist/chunk-AHPGAESX.js +113 -0
- package/vendor/framey/dist/chunk-AHX34EFP.js +15 -0
- package/vendor/framey/dist/chunk-AJ5CSAEZ.js +487 -0
- package/vendor/framey/dist/chunk-B7RJV74O.js +5047 -0
- package/vendor/framey/dist/chunk-BL4U6U52.js +254 -0
- package/vendor/framey/dist/chunk-BSQ7XANM.js +210 -0
- package/vendor/framey/dist/chunk-BZ56MTDX.js +6 -0
- package/vendor/framey/dist/chunk-C5BO7PMT.js +282 -0
- package/vendor/framey/dist/chunk-CFUCHO4T.js +358 -0
- package/vendor/framey/dist/chunk-CZGHFATT.js +31 -0
- package/vendor/framey/dist/chunk-DLA44CMS.js +5649 -0
- package/vendor/framey/dist/chunk-DPVXBVLL.js +31292 -0
- package/vendor/framey/dist/chunk-DQBY7FRR.js +397 -0
- package/vendor/framey/dist/chunk-EAMLBAHA.js +154 -0
- package/vendor/framey/dist/chunk-EOVYQ3IG.js +143 -0
- package/vendor/framey/dist/chunk-EVMY7IPL.js +612 -0
- package/vendor/framey/dist/chunk-FG4BBFZI.js +122 -0
- package/vendor/framey/dist/chunk-FI4I7CME.js +56 -0
- package/vendor/framey/dist/chunk-FLREUEHW.js +2081 -0
- package/vendor/framey/dist/chunk-FUFP7A7S.js +1126 -0
- package/vendor/framey/dist/chunk-FV3PIKGH.js +56 -0
- package/vendor/framey/dist/chunk-FXMKHHPO.js +123 -0
- package/vendor/framey/dist/chunk-GGZAM5VH.js +1187 -0
- package/vendor/framey/dist/chunk-GKZEVJIB.js +39 -0
- package/vendor/framey/dist/chunk-GMECXFAN.js +57 -0
- package/vendor/framey/dist/chunk-GNEFDLFB.js +621 -0
- package/vendor/framey/dist/chunk-GPMLSOEG.js +19274 -0
- package/vendor/framey/dist/chunk-GPYVT5EE.js +59 -0
- package/vendor/framey/dist/chunk-H6IEMZEV.js +40 -0
- package/vendor/framey/dist/chunk-HEFQCIXR.js +170 -0
- package/vendor/framey/dist/chunk-HFQ4CWHA.js +118 -0
- package/vendor/framey/dist/chunk-HPDTCOKE.js +304 -0
- package/vendor/framey/dist/chunk-HTUHQKDB.js +548 -0
- package/vendor/framey/dist/chunk-HU2SPFNG.js +103 -0
- package/vendor/framey/dist/chunk-HVW7ABXD.js +19 -0
- package/vendor/framey/dist/chunk-IG7NDKXO.js +26 -0
- package/vendor/framey/dist/chunk-II3J3WXK.js +246 -0
- package/vendor/framey/dist/chunk-IR35BCLK.js +13 -0
- package/vendor/framey/dist/chunk-ISN5GRYI.js +20 -0
- package/vendor/framey/dist/chunk-IUONB55J.js +669 -0
- package/vendor/framey/dist/chunk-IVSUQR2B.js +161 -0
- package/vendor/framey/dist/chunk-IXU6PQSW.js +259 -0
- package/vendor/framey/dist/chunk-IZCW2NKT.js +29 -0
- package/vendor/framey/dist/chunk-JGXJU5PF.js +68 -0
- package/vendor/framey/dist/chunk-JHOCK3DG.js +27 -0
- package/vendor/framey/dist/chunk-JVBRJMOP.js +121 -0
- package/vendor/framey/dist/chunk-K4MJX7SR.js +17 -0
- package/vendor/framey/dist/chunk-K4WKGXRC.js +18 -0
- package/vendor/framey/dist/chunk-KGPLIH7B.js +127 -0
- package/vendor/framey/dist/chunk-KLIKCX6Q.js +63 -0
- package/vendor/framey/dist/chunk-KXTKKBOC.js +61 -0
- package/vendor/framey/dist/chunk-KZI44OPA.js +185 -0
- package/vendor/framey/dist/chunk-LEKZNRKI.js +45 -0
- package/vendor/framey/dist/chunk-LFGJWOVA.js +61 -0
- package/vendor/framey/dist/chunk-LLMSX6V6.js +17 -0
- package/vendor/framey/dist/chunk-LTGTN2TH.js +195 -0
- package/vendor/framey/dist/chunk-M2WBOQ2Q.js +54 -0
- package/vendor/framey/dist/chunk-MI5FTLYK.js +399 -0
- package/vendor/framey/dist/chunk-MLQP2QUF.js +1809 -0
- package/vendor/framey/dist/chunk-MMQ52NIZ.js +956 -0
- package/vendor/framey/dist/chunk-ND46XKK3.js +742 -0
- package/vendor/framey/dist/chunk-NYEXNSHM.js +504 -0
- package/vendor/framey/dist/chunk-O4YRB27L.js +57 -0
- package/vendor/framey/dist/chunk-O7BGSQ5F.js +92 -0
- package/vendor/framey/dist/chunk-OFQWTKPW.js +117 -0
- package/vendor/framey/dist/chunk-P5ZBLXJ6.js +91 -0
- package/vendor/framey/dist/chunk-P6LJ4Y32.js +4662 -0
- package/vendor/framey/dist/chunk-PKZ7RK5N.js +281 -0
- package/vendor/framey/dist/chunk-PURHAXFL.js +15 -0
- package/vendor/framey/dist/chunk-Q5X2B5WT.js +29 -0
- package/vendor/framey/dist/chunk-QAMKOG67.js +248 -0
- package/vendor/framey/dist/chunk-R6YBK4XX.js +514 -0
- package/vendor/framey/dist/chunk-RGLQURIX.js +2997 -0
- package/vendor/framey/dist/chunk-RNV3DM4S.js +3573 -0
- package/vendor/framey/dist/chunk-ROEW726S.js +4638 -0
- package/vendor/framey/dist/chunk-RQVDTPGD.js +6 -0
- package/vendor/framey/dist/chunk-RVZHGOGR.js +26 -0
- package/vendor/framey/dist/chunk-RXATE6FZ.js +385 -0
- package/vendor/framey/dist/chunk-S3S4TGEH.js +424 -0
- package/vendor/framey/dist/chunk-SDYDNHEE.js +11202 -0
- package/vendor/framey/dist/chunk-SSWBU67I.js +173 -0
- package/vendor/framey/dist/chunk-ST35C4K6.js +28 -0
- package/vendor/framey/dist/chunk-SZNTYTIV.js +990 -0
- package/vendor/framey/dist/chunk-T2EUWCJE.js +29 -0
- package/vendor/framey/dist/chunk-T6AGUTZJ.js +444 -0
- package/vendor/framey/dist/chunk-THJALHFJ.js +11131 -0
- package/vendor/framey/dist/chunk-TIQB63MX.js +18128 -0
- package/vendor/framey/dist/chunk-U242RTCT.js +134 -0
- package/vendor/framey/dist/chunk-UJZREAV6.js +3901 -0
- package/vendor/framey/dist/chunk-UMD3V7CU.js +264 -0
- package/vendor/framey/dist/chunk-UOFAE5RF.js +42 -0
- package/vendor/framey/dist/chunk-UTEIUGAZ.js +29 -0
- package/vendor/framey/dist/chunk-VAS6HQX3.js +138 -0
- package/vendor/framey/dist/chunk-VC76R6W2.js +27 -0
- package/vendor/framey/dist/chunk-VFZ7W5W7.js +226 -0
- package/vendor/framey/dist/chunk-VHNLZAE2.js +348 -0
- package/vendor/framey/dist/chunk-VMC4WSH5.js +278 -0
- package/vendor/framey/dist/chunk-VORQIH64.js +108 -0
- package/vendor/framey/dist/chunk-VTXFRIHO.js +186 -0
- package/vendor/framey/dist/chunk-W2LGJSEN.js +182 -0
- package/vendor/framey/dist/chunk-WKBM7NDG.js +32 -0
- package/vendor/framey/dist/chunk-WMQNIQDJ.js +2057 -0
- package/vendor/framey/dist/chunk-WN4XZC5F.js +504 -0
- package/vendor/framey/dist/chunk-WQ6ZXHFQ.js +133 -0
- package/vendor/framey/dist/chunk-X7HBSTCZ.js +53 -0
- package/vendor/framey/dist/chunk-X7MTZ4T2.js +6 -0
- package/vendor/framey/dist/chunk-XPTOJZJF.js +308 -0
- package/vendor/framey/dist/chunk-XT6MXC7A.js +6 -0
- package/vendor/framey/dist/chunk-XT7X6QCP.js +6 -0
- package/vendor/framey/dist/chunk-XTMDWNEC.js +2820 -0
- package/vendor/framey/dist/chunk-XUHOIDKG.js +6 -0
- package/vendor/framey/dist/chunk-XX7LTW5Y.js +138 -0
- package/vendor/framey/dist/chunk-Y3YGTZEG.js +382 -0
- package/vendor/framey/dist/chunk-Y5SBSGN7.js +346 -0
- package/vendor/framey/dist/chunk-YCMVI4MZ.js +625 -0
- package/vendor/framey/dist/chunk-YOR4CTCE.js +261 -0
- package/vendor/framey/dist/chunk-YPRBGITP.js +2748 -0
- package/vendor/framey/dist/chunk-ZKDMAOFU.js +32 -0
- package/vendor/framey/dist/chunk-ZW6DURPF.js +1318 -0
- package/vendor/framey/dist/clean-ZJOGDKDM.js +336 -0
- package/vendor/framey/dist/cli.js +439 -0
- package/vendor/framey/dist/cloudrun-HGJ3B6ZV.js +767 -0
- package/vendor/framey/dist/commands/contrast-audit.browser.js +643 -0
- package/vendor/framey/dist/commands/layout-audit.browser.js +2914 -0
- package/vendor/framey/dist/commands/motion-sample.browser.js +74 -0
- package/vendor/framey/dist/commands/motion-signature.browser.js +475 -0
- package/vendor/framey/dist/compare-W7UY3BAZ.js +463 -0
- package/vendor/framey/dist/compiler-TM3W6GYF.js +136 -0
- package/vendor/framey/dist/component-QF5VGYMU.js +176 -0
- package/vendor/framey/dist/compositions-XT3PMNDF.js +238 -0
- package/vendor/framey/dist/contactSheet-7W6GYHBQ.js +24 -0
- package/vendor/framey/dist/deploy-6FJPWKF2.js +299 -0
- package/vendor/framey/dist/destroy-HE2RIPKZ.js +57 -0
- package/vendor/framey/dist/deterministicFonts-RADD3BSX.js +50 -0
- package/vendor/framey/dist/dist-4S2FGGVY.js +422 -0
- package/vendor/framey/dist/dist-BE4B2EZE.js +32 -0
- package/vendor/framey/dist/dist-EWXJGN63.js +129 -0
- package/vendor/framey/dist/docs-VKIRFEEX.js +131 -0
- package/vendor/framey/dist/doctor-2UNJ2G7U.js +74 -0
- package/vendor/framey/dist/dom-37K6YOH4.js +14 -0
- package/vendor/framey/dist/dotEnv-7PZQTVIM.js +54 -0
- package/vendor/framey/dist/events-ACY3P2PA.js +76 -0
- package/vendor/framey/dist/events-IHB3B2MK.js +69 -0
- package/vendor/framey/dist/ffmpeg-DR5LGGSK.js +63 -0
- package/vendor/framey/dist/figma-BODVUPHB.js +63 -0
- package/vendor/framey/dist/fontData.generated-NXAOI4GH.js +13 -0
- package/vendor/framey/dist/fontLocalizeCli.js +142 -0
- package/vendor/framey/dist/gpuPolicy-XLEXORZY.js +49 -0
- package/vendor/framey/dist/grade-compare-GAXFQ74C.js +661 -0
- package/vendor/framey/dist/gsapParser-IEUSLYDJ.js +2885 -0
- package/vendor/framey/dist/gsapParserAcorn-LZJKCFQI.js +30 -0
- package/vendor/framey/dist/help-VCB5X2HS.js +184 -0
- package/vendor/framey/dist/history-ENGZEVFT.js +471 -0
- package/vendor/framey/dist/hyperframe.manifest.json +22 -0
- package/vendor/framey/dist/hyperframe.runtime.iife.js +1132 -0
- package/vendor/framey/dist/hyperframes-player.global.js +478 -0
- package/vendor/framey/dist/hyperframes-slideshow.global.js +148 -0
- package/vendor/framey/dist/info-WPCJHBMU.js +182 -0
- package/vendor/framey/dist/init-G4WGLYGD.js +1176 -0
- package/vendor/framey/dist/inspect-5IQ6CNEQ.js +106 -0
- package/vendor/framey/dist/installer-ML4HWUV7.js +40 -0
- package/vendor/framey/dist/keyframes-Q6XJO7VW.js +2755 -0
- package/vendor/framey/dist/lambda-A7JC74JF.js +468 -0
- package/vendor/framey/dist/layout-YET6XNXA.js +95 -0
- package/vendor/framey/dist/lint-I7FZN2VJ.js +162 -0
- package/vendor/framey/dist/lintProject-PDB3UN72.js +30 -0
- package/vendor/framey/dist/manager-CD6LJ7ZN.js +45 -0
- package/vendor/framey/dist/manager-WA4F2GST.js +67 -0
- package/vendor/framey/dist/media-treatment-BXYS4WVA.js +1030 -0
- package/vendor/framey/dist/media-use-LWH3MANK.js +113 -0
- package/vendor/framey/dist/models-K363FYM4.js +195 -0
- package/vendor/framey/dist/motionShot-GPAVWXLH.js +640 -0
- package/vendor/framey/dist/normalize-FASIPBUM.js +24 -0
- package/vendor/framey/dist/normalize-audio-VDWYRHN6.js +268 -0
- package/vendor/framey/dist/open-E3TB6P6J.js +128 -0
- package/vendor/framey/dist/parakeetRunner-UP34UOI2.js +58 -0
- package/vendor/framey/dist/pipeline-VXIUYEBU.js +64 -0
- package/vendor/framey/dist/play-BUTDQ4DV.js +353 -0
- package/vendor/framey/dist/policies-OLDYGFK5.js +309 -0
- package/vendor/framey/dist/preflight-2KZ2GWAZ.js +55 -0
- package/vendor/framey/dist/present-GXYSM7NX.js +353 -0
- package/vendor/framey/dist/preview-EKZ33JOJ.js +1910 -0
- package/vendor/framey/dist/producer-SKU5EV3J.js +13 -0
- package/vendor/framey/dist/progress-ZWB4Z4ET.js +76 -0
- package/vendor/framey/dist/projectConfig-3MZAYVTS.js +60 -0
- package/vendor/framey/dist/publication-DTKWNF74.js +33 -0
- package/vendor/framey/dist/remove-background-OY5L7XN2.js +265 -0
- package/vendor/framey/dist/render-CDV4GDFX.js +187 -0
- package/vendor/framey/dist/render-YE5YAMOD.js +2620 -0
- package/vendor/framey/dist/render-batch-ZAMFPCLL.js +274 -0
- package/vendor/framey/dist/renderSetupWorker.js +120 -0
- package/vendor/framey/dist/resolver-D5BIPSEW.js +42 -0
- package/vendor/framey/dist/runtimeVersion.js +18 -0
- package/vendor/framey/dist/sam-WFZXI4F7.js +23 -0
- package/vendor/framey/dist/shaderTransitionWorker.js +44 -0
- package/vendor/framey/dist/sherpa-RQWLV7I3.js +79 -0
- package/vendor/framey/dist/sherpaWorker.js +84 -0
- package/vendor/framey/dist/sites-AMOVABTK.js +54 -0
- package/vendor/framey/dist/skill-ZHVLH7KX.js +15 -0
- package/vendor/framey/dist/slideshow-SJGK4RQS.js +24 -0
- package/vendor/framey/dist/snapshot-TUXL6LBQ.js +1515 -0
- package/vendor/framey/dist/src-JHQGB2S6.js +215 -0
- package/vendor/framey/dist/src-Z5PGEMXB.js +532 -0
- package/vendor/framey/dist/state-B6H3WHCF.js +28 -0
- package/vendor/framey/dist/staticProjectServer-DFVZOUGI.js +63 -0
- package/vendor/framey/dist/studio/assets/hyperframes-player-DLaKjw0L.js +478 -0
- package/vendor/framey/dist/studio/assets/index-BX3KHhGX.js +71 -0
- package/vendor/framey/dist/studio/assets/index-CYbvOVp-.css +1 -0
- package/vendor/framey/dist/studio/assets/index-Dp3QEUGL.js +402 -0
- package/vendor/framey/dist/studio/assets/index-qrTAXbqc.js +531 -0
- package/vendor/framey/dist/studio/favicon.svg +14 -0
- package/vendor/framey/dist/studio/icons/timeline/audio.svg +7 -0
- package/vendor/framey/dist/studio/icons/timeline/captions.svg +5 -0
- package/vendor/framey/dist/studio/icons/timeline/composition.svg +12 -0
- package/vendor/framey/dist/studio/icons/timeline/image.svg +18 -0
- package/vendor/framey/dist/studio/icons/timeline/music.svg +10 -0
- package/vendor/framey/dist/studio/icons/timeline/text.svg +3 -0
- package/vendor/framey/dist/studio/index.html +23 -0
- package/vendor/framey/dist/studio-api-2NECGRMB.js +179 -0
- package/vendor/framey/dist/studioSelectionClient-IUUPKUHZ.js +25 -0
- package/vendor/framey/dist/studioServer-N2OPUMOQ.js +1569 -0
- package/vendor/framey/dist/synthesize-PKTB5ATG.js +183 -0
- package/vendor/framey/dist/system-7F3UEHQT.js +27 -0
- package/vendor/framey/dist/telemetry-DQGPEKM6.js +145 -0
- package/vendor/framey/dist/telemetry-UN55N6U6.js +77 -0
- package/vendor/framey/dist/timeline-LHUUMK63.js +1295 -0
- package/vendor/framey/dist/tokens-OLECWDFK.js +92 -0
- package/vendor/framey/dist/transcribe-O7HQKMIV.js +331 -0
- package/vendor/framey/dist/transcribe-UJESPBT5.js +76 -0
- package/vendor/framey/dist/tts-YCCQHZD4.js +234 -0
- package/vendor/framey/dist/urlDownloader-H2LXLG4U.js +48 -0
- package/vendor/framey/dist/usage-HG4WMM7A.js +2167 -0
- package/vendor/framey/dist/validate-F2XMN6EV.js +87 -0
- package/vendor/framey/dist/vendor/npm/gsap@3.15.0/GSAP-LICENSE.txt +8 -0
- package/vendor/framey/dist/vendor/npm/gsap@3.15.0/dist/CustomEase.min.js +11 -0
- package/vendor/framey/dist/vendor/npm/gsap@3.15.0/dist/MotionPathPlugin.min.js +11 -0
- package/vendor/framey/dist/vendor/npm/gsap@3.15.0/dist/gsap.min.js +11 -0
- package/vendor/framey/dist/warmCheck.js +220 -0
- package/vendor/framey/dist/warmCheckClient.js +139 -0
- package/vendor/framey/dist/workspaceBuildCheck-XYD2YTFW.js +133 -0
- package/vendor/framey-player/LICENSE +190 -0
- package/vendor/framey-player/NOTICE +16 -0
- package/vendor/framey-player/SOURCE.json +6 -0
- package/vendor/framey-player/hyperframes-player.global.js +478 -0
- package/vendor/framey.pin +1 -0
- package/bin/index.js +0 -3
|
@@ -0,0 +1,361 @@
|
|
|
1
|
+
import { existsSync, realpathSync } from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import type { DeskResult, SeriesInput } from "../queue/desk-ops.js";
|
|
4
|
+
import { CREDENTIALS_KEY_REFUSAL } from "../queue/desk-ops.js";
|
|
5
|
+
import type { Store } from "../queue/store.js";
|
|
6
|
+
import { engineById, engineStatus, otherReadyEngine, type EngineProbes } from "../product/engines.js";
|
|
7
|
+
import { channelInfo } from "./channel.js";
|
|
8
|
+
import { secretFieldError, type SecretOwnerKind } from "./secrets.js";
|
|
9
|
+
|
|
10
|
+
export type VerbResult =
|
|
11
|
+
| { ok: true; data?: Record<string, unknown> }
|
|
12
|
+
| { ok: false; refusal: string; fix: string; code?: string };
|
|
13
|
+
|
|
14
|
+
const PLATFORMS = ["youtube", "tiktok", "instagram", "facebook"];
|
|
15
|
+
const SOURCE_KINDS = ["rss", "subreddit", "youtubeChannel", "youtubeTrending", "vfrey-topics"];
|
|
16
|
+
/** A niche id from `npx vfrey topics niches`, such as ai-tools or my-niche-1a2b3c. */
|
|
17
|
+
export const NICHE_ID = /^[A-Za-z0-9][A-Za-z0-9._-]{0,79}$/;
|
|
18
|
+
|
|
19
|
+
const good = (data?: Record<string, unknown>): VerbResult => (data ? { ok: true, data } : { ok: true });
|
|
20
|
+
const no = (refusal: string, fix: string): VerbResult => ({ ok: false, refusal, fix });
|
|
21
|
+
const urlRefusal = (value: string): VerbResult => ({
|
|
22
|
+
ok: false,
|
|
23
|
+
code: "source_url_rejected",
|
|
24
|
+
refusal: `source_url_rejected: "${value.slice(0, 80)}" is not an http or https URL.`,
|
|
25
|
+
fix: "Use a feed address that starts with http:// or https://.",
|
|
26
|
+
});
|
|
27
|
+
const isHttpUrl = (value: string): boolean => {
|
|
28
|
+
try {
|
|
29
|
+
const protocol = new URL(value).protocol;
|
|
30
|
+
return protocol === "http:" || protocol === "https:";
|
|
31
|
+
} catch {
|
|
32
|
+
return false;
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
const fromDesk = (result: DeskResult, fix: string, data?: Record<string, unknown>): VerbResult =>
|
|
36
|
+
result.ok ? good(data) : no(result.refusal, fix);
|
|
37
|
+
|
|
38
|
+
const exists = (store: Store, table: "desk_accounts" | "desk_series", id: string): boolean =>
|
|
39
|
+
Boolean(store.db.prepare(`SELECT id FROM ${table} WHERE id = ?`).get(id));
|
|
40
|
+
|
|
41
|
+
function nonNegative(values: Record<string, number>): VerbResult | null {
|
|
42
|
+
for (const [name, value] of Object.entries(values)) {
|
|
43
|
+
if (!Number.isFinite(value) || value < 0) return no(`${name} must be a number of zero or more.`, `Pass ${name} as a number such as 1.`);
|
|
44
|
+
}
|
|
45
|
+
return null;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export function snapshotRead(store: Store, input: { userId: string }): VerbResult {
|
|
49
|
+
return good(store.snapshot(input.userId));
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export function accountCreate(store: Store, input: { id: string; name: string; ownerId?: string | null }): VerbResult {
|
|
53
|
+
if (exists(store, "desk_accounts", input.id)) return no(`Account ${input.id} already exists.`, "Pick another id, or rename the existing account.");
|
|
54
|
+
store.createAccount(input);
|
|
55
|
+
return good();
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export function accountRename(store: Store, input: { id: string; name: string }): VerbResult {
|
|
59
|
+
return fromDesk(store.renameAccount(input.id, input.name), "Check the account id in `vfrey desk snapshot`.");
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export function accountDelete(store: Store, input: { id: string }): VerbResult {
|
|
63
|
+
return fromDesk(store.deleteAccount(input.id), "Cancel or finish the account's open job, then delete it again.");
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export function secretSet(
|
|
67
|
+
store: Store,
|
|
68
|
+
input: { ownerKind: SecretOwnerKind; ownerId: string; field: string; value: string; key: string | undefined },
|
|
69
|
+
): VerbResult {
|
|
70
|
+
const bad = secretFieldError(input.ownerKind, input.field);
|
|
71
|
+
if (bad) return no(bad.refusal, bad.fix);
|
|
72
|
+
const saved = store.setSecret({
|
|
73
|
+
ownerKind: input.ownerKind,
|
|
74
|
+
ownerId: input.ownerId,
|
|
75
|
+
field: input.field,
|
|
76
|
+
plaintext: input.value,
|
|
77
|
+
key: input.key ?? "",
|
|
78
|
+
});
|
|
79
|
+
if ("ok" in saved) {
|
|
80
|
+
return saved.ok
|
|
81
|
+
? good({ field: input.field, present: true })
|
|
82
|
+
: no(saved.refusal, saved.refusal === CREDENTIALS_KEY_REFUSAL ? "export CREDENTIALS_KEY=$(openssl rand -base64 32)" : "Check the value and try again.");
|
|
83
|
+
}
|
|
84
|
+
return good({ field: input.field, present: saved.present });
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export function secretClear(store: Store, input: { ownerKind: SecretOwnerKind; ownerId: string; field: string }): VerbResult {
|
|
88
|
+
const bad = secretFieldError(input.ownerKind, input.field);
|
|
89
|
+
if (bad) return no(bad.refusal, bad.fix);
|
|
90
|
+
const cleared = store.clearSecret(input);
|
|
91
|
+
return good({ field: input.field, present: cleared.present });
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/** Stores the machine default engine. A refusal names the reason and another ready engine. */
|
|
95
|
+
export function engineUse(store: Store, input: { engine: string; probes: EngineProbes }): VerbResult {
|
|
96
|
+
let spec;
|
|
97
|
+
try {
|
|
98
|
+
spec = engineById(input.engine);
|
|
99
|
+
} catch {
|
|
100
|
+
return no(`${input.engine} is not an engine.`, "Run `npx vfrey engine list` to see the engines.");
|
|
101
|
+
}
|
|
102
|
+
const status = engineStatus(spec, input.probes);
|
|
103
|
+
if (status.availability === "unavailable") {
|
|
104
|
+
const other = otherReadyEngine(spec.id, input.probes);
|
|
105
|
+
return no(
|
|
106
|
+
`${spec.label} is unavailable: ${status.reason}.`,
|
|
107
|
+
other ? `Choose ${other.label}: \`npx vfrey engine use ${other.id}\`.` : "Run `npx vfrey doctor` to see what is missing.",
|
|
108
|
+
);
|
|
109
|
+
}
|
|
110
|
+
store.setEngineDefault(spec.id);
|
|
111
|
+
return good({ ok: true, engine: spec.id, label: spec.label });
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
export function seriesCreate(store: Store, input: SeriesInput): VerbResult {
|
|
115
|
+
if (!input.niche.trim()) return no("A series needs a niche.", "Pass --niche with one sentence about the topic.");
|
|
116
|
+
if (exists(store, "desk_series", input.id)) return no(`Series ${input.id} already exists.`, "Pick another id, or update the existing series.");
|
|
117
|
+
if (input.engine) {
|
|
118
|
+
const bad = engineProblem(input.engine);
|
|
119
|
+
if (bad) return bad;
|
|
120
|
+
input = { ...input, engine: input.engine === "default" ? "" : engineById(input.engine).id };
|
|
121
|
+
}
|
|
122
|
+
store.createSeries(input);
|
|
123
|
+
return good();
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function engineProblem(value: string): VerbResult | null {
|
|
127
|
+
if (value === "" || value === "default") return null;
|
|
128
|
+
try {
|
|
129
|
+
engineById(value);
|
|
130
|
+
return null;
|
|
131
|
+
} catch {
|
|
132
|
+
return no(`${value} is not an engine.`, "Use remotion, hyperframes, vfrey-framey, comfyui, or default to follow the machine default.");
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
export function seriesUpdate(store: Store, input: { id: string; patch: Partial<SeriesInput> }): VerbResult {
|
|
137
|
+
if (input.patch.engine !== undefined) {
|
|
138
|
+
const bad = engineProblem(input.patch.engine);
|
|
139
|
+
if (bad) return bad;
|
|
140
|
+
if (input.patch.engine === "default") input.patch = { ...input.patch, engine: "" };
|
|
141
|
+
else if (input.patch.engine) input.patch = { ...input.patch, engine: engineById(input.patch.engine).id };
|
|
142
|
+
}
|
|
143
|
+
return fromDesk(store.updateSeries(input.id, input.patch), "Check the series id in `vfrey desk snapshot`.");
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
export function seriesDelete(store: Store, input: { id: string }): VerbResult {
|
|
147
|
+
return fromDesk(store.deleteSeries(input.id), "Cancel or finish the series' open job, then delete it again.");
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Takes a series out of dry-run. The person types the channel or page id of one of its platforms. A mismatch refuses.
|
|
152
|
+
* When no id is on file for any enabled platform, the typed id is stored as the YouTube channel id (or the first enabled
|
|
153
|
+
* platform's) and the series goes live with it.
|
|
154
|
+
*/
|
|
155
|
+
export function seriesLive(store: Store, input: { id: string; confirm: string | undefined; key: string | undefined }): VerbResult {
|
|
156
|
+
if (!exists(store, "desk_series", input.id)) return no(`Series ${input.id} was not found.`, "Check the series id in `vfrey desk snapshot`.");
|
|
157
|
+
const typed = (input.confirm ?? "").trim();
|
|
158
|
+
const channels = channelInfo(store, input.id, input.key).filter((item) => item.enabled);
|
|
159
|
+
if (channels.length === 0) return no("No platform is on for this series, so there is nothing to go live on.", "Turn a platform on for the series first.");
|
|
160
|
+
if (!typed) {
|
|
161
|
+
return no("Going live needs the channel id typed once.", `Pass --confirm <channel id> with the id of the channel or page this series posts to (${channels.map((c) => c.platform).join(", ")}).`);
|
|
162
|
+
}
|
|
163
|
+
const known = channels.filter((item) => item.id !== null);
|
|
164
|
+
let matched: { platform: string; saved: boolean };
|
|
165
|
+
if (known.length > 0) {
|
|
166
|
+
const hit = known.find((item) => item.id === typed);
|
|
167
|
+
if (!hit) {
|
|
168
|
+
return no(
|
|
169
|
+
`That is not the channel id on file for ${input.id}.`,
|
|
170
|
+
"Copy the id shown for the account on the series page (or `desk snapshot`), then run `npx vfrey desk series live --id " + input.id + " --confirm <channel id>` again.",
|
|
171
|
+
);
|
|
172
|
+
}
|
|
173
|
+
matched = { platform: hit.platform, saved: false };
|
|
174
|
+
} else {
|
|
175
|
+
const target = channels[0]!;
|
|
176
|
+
const row = store.db.prepare(`SELECT config_json FROM desk_platforms WHERE series_id = ? AND platform = ?`).get(input.id, target.platform) as { config_json: string } | undefined;
|
|
177
|
+
let config: Record<string, unknown> = {};
|
|
178
|
+
try {
|
|
179
|
+
config = row ? (JSON.parse(row.config_json) as Record<string, unknown>) : {};
|
|
180
|
+
} catch {
|
|
181
|
+
config = {};
|
|
182
|
+
}
|
|
183
|
+
const saved = store.setPlatform({ seriesId: input.id, platform: target.platform, enabled: true, config: { ...config, channelId: typed } });
|
|
184
|
+
if (!saved.ok) return no(saved.refusal, "Check the id and try again.");
|
|
185
|
+
matched = { platform: target.platform, saved: true };
|
|
186
|
+
}
|
|
187
|
+
const result = store.setLive(
|
|
188
|
+
input.id,
|
|
189
|
+
true,
|
|
190
|
+
`Went live on ${matched.platform} (channel id ${typed}${matched.saved ? ", saved as the id on file" : ""}).`,
|
|
191
|
+
);
|
|
192
|
+
return fromDesk(result, "Check the series id in `vfrey desk snapshot`.", { id: input.id, live: true, platform: matched.platform, channelIdSaved: matched.saved });
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/** Returns a series to dry-run. No confirm is needed. */
|
|
196
|
+
export function seriesOff(store: Store, input: { id: string }): VerbResult {
|
|
197
|
+
return fromDesk(store.setLive(input.id, false, "Back to dry-run by the person."), "Check the series id in `vfrey desk snapshot`.", { id: input.id, live: false });
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
export function seriesPause(store: Store, input: { id: string }): VerbResult {
|
|
201
|
+
return fromDesk(store.pauseSeries(input.id), "Check the series id in `vfrey desk snapshot`.");
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
export function seriesResume(store: Store, input: { id: string }): VerbResult {
|
|
205
|
+
return fromDesk(store.resumeSeries(input.id), "Check the series id in `vfrey desk snapshot`.");
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
export function cadenceSet(
|
|
209
|
+
store: Store,
|
|
210
|
+
input: { seriesId: string; timezone: string; minHoursBetweenPosts: number; maxPostsPerDay: number; windows: string[] },
|
|
211
|
+
): VerbResult {
|
|
212
|
+
const invalid = nonNegative({ minHoursBetweenPosts: input.minHoursBetweenPosts, maxPostsPerDay: input.maxPostsPerDay });
|
|
213
|
+
if (invalid) return invalid;
|
|
214
|
+
return fromDesk(
|
|
215
|
+
store.setCadence(input.seriesId, {
|
|
216
|
+
timezone: input.timezone,
|
|
217
|
+
minHoursBetweenPosts: input.minHoursBetweenPosts,
|
|
218
|
+
maxPostsPerDay: input.maxPostsPerDay,
|
|
219
|
+
windows: input.windows,
|
|
220
|
+
}),
|
|
221
|
+
"Check the series id in `vfrey desk snapshot`.",
|
|
222
|
+
);
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
export function cadenceClear(store: Store): VerbResult {
|
|
226
|
+
return fromDesk(store.clearCadence(), "Change the values with `vfrey desk cadence set`.");
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
export function quotaSet(
|
|
230
|
+
store: Store,
|
|
231
|
+
input: { seriesId: string; youtubeDailyUploadCap: number; maxRepliesPerDay: number; minSecondsBetween: number },
|
|
232
|
+
): VerbResult {
|
|
233
|
+
const invalid = nonNegative({
|
|
234
|
+
youtubeDailyUploadCap: input.youtubeDailyUploadCap,
|
|
235
|
+
maxRepliesPerDay: input.maxRepliesPerDay,
|
|
236
|
+
minSecondsBetween: input.minSecondsBetween,
|
|
237
|
+
});
|
|
238
|
+
if (invalid) return invalid;
|
|
239
|
+
return fromDesk(
|
|
240
|
+
store.setQuota(input.seriesId, {
|
|
241
|
+
youtubeDailyUploadCap: input.youtubeDailyUploadCap,
|
|
242
|
+
maxRepliesPerDay: input.maxRepliesPerDay,
|
|
243
|
+
minSecondsBetween: input.minSecondsBetween,
|
|
244
|
+
}),
|
|
245
|
+
"Check the series id in `vfrey desk snapshot`.",
|
|
246
|
+
);
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
export function quotaClear(store: Store): VerbResult {
|
|
250
|
+
return fromDesk(store.clearQuota(), "Set the cap to 0 with `vfrey desk quota set`.");
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
export function platformSet(
|
|
254
|
+
store: Store,
|
|
255
|
+
input: { seriesId: string; platform: string; enabled: boolean; config?: Record<string, unknown> },
|
|
256
|
+
): VerbResult {
|
|
257
|
+
if (!PLATFORMS.includes(input.platform)) return no(`${input.platform} is not a platform.`, `Use one of: ${PLATFORMS.join(", ")}.`);
|
|
258
|
+
return fromDesk(store.setPlatform(input), "Put credentials in `vfrey desk secret set`, not in platform config.");
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
export function platformDisable(store: Store, input: { seriesId: string; platform: string }): VerbResult {
|
|
262
|
+
return fromDesk(store.disablePlatform(input.seriesId, input.platform), "Check the series and platform in `vfrey desk snapshot`.");
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
export function platformRemove(store: Store, input: { seriesId: string; platform: string }): VerbResult {
|
|
266
|
+
return fromDesk(store.removePlatform(input.seriesId, input.platform), "Disable the platform with `vfrey desk platform disable`.");
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
export function sourceAdd(store: Store, input: { seriesId: string; kind: string; value: string }): VerbResult {
|
|
270
|
+
if (!SOURCE_KINDS.includes(input.kind)) return no(`${input.kind} is not a source kind.`, `Use one of: ${SOURCE_KINDS.join(", ")}.`);
|
|
271
|
+
if (!input.value.trim()) return no("A source needs a value.", "Pass --value with a feed URL, subreddit, channel id, or region.");
|
|
272
|
+
if (!exists(store, "desk_series", input.seriesId)) return no(`Series ${input.seriesId} was not found.`, "Check the series id in `vfrey desk snapshot`.");
|
|
273
|
+
if (input.kind === "rss" && !isHttpUrl(input.value.trim())) return urlRefusal(input.value.trim());
|
|
274
|
+
if (input.kind === "vfrey-topics" && !NICHE_ID.test(input.value.trim())) return no(`"${input.value.trim()}" is not a niche id.`, "Pass --value with an id from `npx vfrey topics niches`.");
|
|
275
|
+
return good({ ok: true, ...store.addSource({ ...input, value: input.value.trim() }) });
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
export function sourceUpdate(store: Store, input: { id: string; value: string }): VerbResult {
|
|
279
|
+
const row = store.db.prepare(`SELECT kind FROM desk_sources WHERE id = ?`).get(input.id) as { kind: string } | undefined;
|
|
280
|
+
if (row?.kind === "rss" && !isHttpUrl(input.value.trim())) return urlRefusal(input.value.trim());
|
|
281
|
+
return fromDesk(store.updateSource(input.id, input.value), "Check the source id in `vfrey desk snapshot`.");
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
export function sourceRemove(store: Store, input: { id: string }): VerbResult {
|
|
285
|
+
return fromDesk(store.removeSource(input.id), "Check the source id in `vfrey desk snapshot`.");
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
export function modeSet(store: Store, input: { userId: string; mode: string; llmKeyPresent: boolean }): VerbResult {
|
|
289
|
+
return fromDesk(
|
|
290
|
+
store.setMode(input.userId, input.mode, input.llmKeyPresent),
|
|
291
|
+
input.mode === "api" ? "export LLM_API_KEY=<key>, or use mode agent." : "Use `vfrey desk mode set agent` or `api`.",
|
|
292
|
+
{ ok: true, mode: input.mode },
|
|
293
|
+
);
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
export function modeClear(store: Store, input: { userId: string }): VerbResult {
|
|
297
|
+
return fromDesk(store.clearMode(input.userId), "Run it again.");
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
export function jobRetry(store: Store, input: { id: string }): VerbResult {
|
|
301
|
+
return fromDesk(store.retryJob(input.id), "Cancel a leased job first, or check the id in `vfrey desk snapshot`.");
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
/** Releases a job that waits for review. The next tick renders it and the publish rules run as they always do. */
|
|
305
|
+
export function jobApprove(store: Store, input: { id: string }): VerbResult {
|
|
306
|
+
return fromDesk(store.approveJob(input.id), "Only a job that waits for review can be approved. Check `npx vfrey desk snapshot --json` for awaiting_review jobs.", {
|
|
307
|
+
id: input.id,
|
|
308
|
+
status: "pending",
|
|
309
|
+
next: "The next tick renders it: npx vfrey tick (or let the worker do it).",
|
|
310
|
+
});
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
/** Ends a job that waits for review. It is `rejected` with the code `review_rejected` and never renders. */
|
|
314
|
+
export function jobReject(store: Store, input: { id: string; reason?: string | null }): VerbResult {
|
|
315
|
+
return fromDesk(store.rejectJob(input.id, input.reason ?? null), "Only a job that waits for review can be rejected. Check `npx vfrey desk snapshot --json` for awaiting_review jobs.", {
|
|
316
|
+
id: input.id,
|
|
317
|
+
status: "rejected",
|
|
318
|
+
code: "review_rejected",
|
|
319
|
+
});
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
export function jobCancel(store: Store, input: { id: string }): VerbResult {
|
|
323
|
+
return fromDesk(store.cancelJob(input.id), "Wait for the job to start, or retry it instead.");
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
export function warningAck(store: Store, input: { id: string }): VerbResult {
|
|
327
|
+
return fromDesk(store.ackWarning(input.id), "Check the warning id in `vfrey desk snapshot`.");
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
export function warningDelete(store: Store): VerbResult {
|
|
331
|
+
return fromDesk(store.deleteWarning(), "Acknowledge it with `vfrey desk warning ack`.");
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
export function jobDelete(store: Store): VerbResult {
|
|
335
|
+
return fromDesk(store.deleteJob(), "Cancel a leased job with `vfrey desk job cancel`.");
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
export function publicationDelete(store: Store): VerbResult {
|
|
339
|
+
return fromDesk(store.deletePublication(), "Leave the row in place. It is the disclosure proof.");
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
export function metricDelete(store: Store): VerbResult {
|
|
343
|
+
return fromDesk(store.deleteMetric(), "Leave the snapshots in place. New readings add rows.");
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
export function commentDelete(store: Store): VerbResult {
|
|
347
|
+
return fromDesk(store.deleteComment(), "Leave the ledger in place. It stops a comment from being answered twice.");
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
export function deskImport(store: Store, input: { from: string }): VerbResult {
|
|
351
|
+
const from = path.resolve(input.from);
|
|
352
|
+
const file = path.join(from, "pipeline.sqlite");
|
|
353
|
+
if (!existsSync(file)) {
|
|
354
|
+
return no(`No pipeline.sqlite in ${from}.`, "Pass --from <folder> with the old data folder, such as ./data.");
|
|
355
|
+
}
|
|
356
|
+
if (store.dataDir && path.join(realpathSync(from), "pipeline.sqlite") === path.join(realpathSync(store.dataDir), "pipeline.sqlite")) {
|
|
357
|
+
return no("That folder holds the database already in use.", "Pass the old per-folder data directory, not the vfrey home.");
|
|
358
|
+
}
|
|
359
|
+
const result = store.importLegacy(from);
|
|
360
|
+
return good({ ok: true, imported: result.imported, files: result.files });
|
|
361
|
+
}
|
package/src/doctor.ts
ADDED
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
import { accessSync, constants, existsSync } from "node:fs";
|
|
2
|
+
import os from "node:os";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { credentialsKey } from "@vfrey/contracts";
|
|
5
|
+
import { resolveHome, type ResolvedHome } from "./home.js";
|
|
6
|
+
import { installCommand } from "./skill-agents.js";
|
|
7
|
+
import { findSkillCopies } from "./skill-install.js";
|
|
8
|
+
import { cachedHeadlessShell, remotionCacheDir } from "./render/chrome.js";
|
|
9
|
+
import { readMachineEngine } from "./product/engine-choice.js";
|
|
10
|
+
import { allEngineStatuses, DEFAULT_ENGINE, engineById, engineStatus, statusText } from "./product/engines.js";
|
|
11
|
+
import { probeEnginesSync } from "./product/probes.js";
|
|
12
|
+
import { localVoiceProblem } from "./tts.js";
|
|
13
|
+
import { accountLine } from "./api/commands.js";
|
|
14
|
+
|
|
15
|
+
export { cachedHeadlessShell, remotionCacheDir };
|
|
16
|
+
|
|
17
|
+
type Env = Record<string, string | undefined>;
|
|
18
|
+
|
|
19
|
+
export interface DoctorCheck {
|
|
20
|
+
id: string;
|
|
21
|
+
ok: boolean;
|
|
22
|
+
label: string;
|
|
23
|
+
detail: string;
|
|
24
|
+
fix?: string;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export interface DoctorReport {
|
|
28
|
+
ok: boolean;
|
|
29
|
+
dataDir: string;
|
|
30
|
+
checks: DoctorCheck[];
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const MIN_NODE = [22, 13];
|
|
34
|
+
|
|
35
|
+
function nodeCheck(): DoctorCheck {
|
|
36
|
+
const [major = 0, minor = 0] = process.versions.node.split(".").map(Number);
|
|
37
|
+
const ok = major > MIN_NODE[0]! || (major === MIN_NODE[0] && minor >= MIN_NODE[1]!);
|
|
38
|
+
return {
|
|
39
|
+
id: "node",
|
|
40
|
+
ok,
|
|
41
|
+
label: "Node version",
|
|
42
|
+
detail: `Node ${process.versions.node}`,
|
|
43
|
+
...(ok ? {} : { fix: `Install Node ${MIN_NODE.join(".")} or newer.` }),
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/** The nearest folder that exists, so the check never creates the data folder. */
|
|
48
|
+
function nearestExisting(dir: string): string {
|
|
49
|
+
let current = dir;
|
|
50
|
+
while (!existsSync(current)) {
|
|
51
|
+
const parent = path.dirname(current);
|
|
52
|
+
if (parent === current) break;
|
|
53
|
+
current = parent;
|
|
54
|
+
}
|
|
55
|
+
return current;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function homeCheck(dataDir: string): DoctorCheck {
|
|
59
|
+
const target = nearestExisting(dataDir);
|
|
60
|
+
try {
|
|
61
|
+
accessSync(target, constants.W_OK);
|
|
62
|
+
return { id: "home", ok: true, label: "Home directory", detail: `${dataDir} is writable` };
|
|
63
|
+
} catch {
|
|
64
|
+
return {
|
|
65
|
+
id: "home",
|
|
66
|
+
ok: false,
|
|
67
|
+
label: "Home directory",
|
|
68
|
+
detail: `${dataDir} is not writable`,
|
|
69
|
+
fix: "Set DATA_DIR or VFREY_HOME to a folder you can write to.",
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function chromeCheck(env: Env, cwd: string): DoctorCheck {
|
|
75
|
+
const label = "Chrome for Remotion";
|
|
76
|
+
const cached = cachedHeadlessShell(cwd);
|
|
77
|
+
if (cached) return { id: "chrome", ok: true, label, detail: `Chrome Headless Shell is cached at ${cached}. The first render needs no download.` };
|
|
78
|
+
const candidates = [
|
|
79
|
+
env.BROWSER_EXECUTABLE,
|
|
80
|
+
"/Applications/Google Chrome.app/Contents/MacOS/Google Chrome",
|
|
81
|
+
"/Applications/Chromium.app/Contents/MacOS/Chromium",
|
|
82
|
+
].filter((item): item is string => Boolean(item));
|
|
83
|
+
for (const dir of (env.PATH ?? "").split(path.delimiter)) {
|
|
84
|
+
for (const name of ["google-chrome", "google-chrome-stable", "chromium", "chromium-browser", "chrome"]) {
|
|
85
|
+
if (dir) candidates.push(path.join(dir, name));
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
const found = candidates.find((candidate) => existsSync(candidate));
|
|
89
|
+
if (found) {
|
|
90
|
+
return {
|
|
91
|
+
id: "chrome",
|
|
92
|
+
ok: true,
|
|
93
|
+
label,
|
|
94
|
+
detail: `System Chrome found at ${found}. Renders use Chrome Headless Shell, not this browser. The first render downloads Chrome Headless Shell (about 170 MB) unless it is already cached. Run \`npx remotion browser ensure\` to fetch it now.`,
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
return {
|
|
98
|
+
id: "chrome",
|
|
99
|
+
ok: false,
|
|
100
|
+
label,
|
|
101
|
+
detail: "No Chrome or Chrome Headless Shell was found",
|
|
102
|
+
fix: "Run `npx remotion browser ensure`, or install Chrome.",
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function voiceCheck(env: Env): DoctorCheck {
|
|
107
|
+
const provider = env.TTS_PROVIDER === "openai" ? "openai" : env.TTS_PROVIDER === "none" ? "none" : "local";
|
|
108
|
+
if (provider === "none") return { id: "voice", ok: true, label: "Voice", detail: "TTS_PROVIDER=none, captions only" };
|
|
109
|
+
if (provider === "openai") {
|
|
110
|
+
const keyed = Boolean(env.TTS_API_KEY || env.LLM_API_KEY);
|
|
111
|
+
return keyed
|
|
112
|
+
? { id: "voice", ok: true, label: "Voice", detail: "TTS_PROVIDER=openai with a key" }
|
|
113
|
+
: { id: "voice", ok: false, label: "Voice", detail: "TTS_PROVIDER=openai has no TTS_API_KEY", fix: "Set TTS_API_KEY, or set TTS_PROVIDER=none." };
|
|
114
|
+
}
|
|
115
|
+
const problem = localVoiceProblem(env);
|
|
116
|
+
if (problem) return { id: "voice", ok: false, label: "Voice", detail: problem.message, fix: problem.fix };
|
|
117
|
+
return { id: "voice", ok: true, label: "Voice", detail: "Local voice (say and ffmpeg) is available" };
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function credentialsKeyCheck(env: Env): DoctorCheck {
|
|
121
|
+
const raw = env.CREDENTIALS_KEY;
|
|
122
|
+
if (!raw) return { id: "credentials_key", ok: true, label: "CREDENTIALS_KEY", detail: "Not set. Dashboard secrets stay off." };
|
|
123
|
+
try {
|
|
124
|
+
credentialsKey(raw);
|
|
125
|
+
return { id: "credentials_key", ok: true, label: "CREDENTIALS_KEY", detail: "Valid" };
|
|
126
|
+
} catch {
|
|
127
|
+
return {
|
|
128
|
+
id: "credentials_key",
|
|
129
|
+
ok: false,
|
|
130
|
+
label: "CREDENTIALS_KEY",
|
|
131
|
+
detail: "Not 32 bytes of base64",
|
|
132
|
+
fix: "Generate one with `openssl rand -base64 32`.",
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/** Which data folder this run works on. Information only: it never fails. */
|
|
138
|
+
function projectCheck(home: ResolvedHome): DoctorCheck {
|
|
139
|
+
const detail = home.project
|
|
140
|
+
? `Project ${home.project.name} at ${home.project.root}`
|
|
141
|
+
: home.source === "machine"
|
|
142
|
+
? "Not in a vfrey project; using the machine desk. Start one: npm create vfrey@latest my-channel"
|
|
143
|
+
: `Data folder set by ${home.source}`;
|
|
144
|
+
return { id: "project", ok: true, label: "Project", detail };
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
function skillCheck(env: Env, cwd: string, homedir: string): DoctorCheck {
|
|
148
|
+
const label = "Agent skill";
|
|
149
|
+
const copies = findSkillCopies(env, cwd, homedir);
|
|
150
|
+
const ours = copies.filter((copy) => copy.state !== "foreign");
|
|
151
|
+
const describe = (copy: (typeof copies)[number]) =>
|
|
152
|
+
`${copy.label} ${copy.kind} copy at ${copy.file} (${copy.state === "stale" ? (copy.was === "unversioned" ? "stale, unversioned" : `stale, version ${copy.was}`) : "up to date"})`;
|
|
153
|
+
if (ours.some((copy) => copy.state === "current")) {
|
|
154
|
+
return { id: "skill", ok: true, label, detail: `vfrey skill installed: ${ours.map(describe).join("; ")}` };
|
|
155
|
+
}
|
|
156
|
+
if (ours.length > 0) {
|
|
157
|
+
const fixes = [...new Set(ours.map((copy) => installCommand(copy.agent, copy.kind)))];
|
|
158
|
+
return {
|
|
159
|
+
id: "skill",
|
|
160
|
+
ok: false,
|
|
161
|
+
label,
|
|
162
|
+
detail: `The vfrey skill is older than this package: ${ours.map(describe).join("; ")}. The agent reads it first, so a stale copy will steer it to old commands.`,
|
|
163
|
+
fix: fixes.join("; "),
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
return {
|
|
167
|
+
id: "skill",
|
|
168
|
+
ok: false,
|
|
169
|
+
label,
|
|
170
|
+
detail: "The vfrey skill is not installed where Claude Code, Codex, Grok, Cursor or Gemini CLI look",
|
|
171
|
+
fix: "npx vfrey skill install --agent <your agent> (claude, codex, grok, cursor, gemini, or all)",
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/** Every engine's readiness, why an unavailable one is, and which is the default. */
|
|
176
|
+
function enginesCheck(env: Env, cwd: string, dataDir: string): DoctorCheck {
|
|
177
|
+
const probes = probeEnginesSync(env, cwd);
|
|
178
|
+
let current = DEFAULT_ENGINE;
|
|
179
|
+
try {
|
|
180
|
+
current = engineById(readMachineEngine(dataDir) ?? DEFAULT_ENGINE).id;
|
|
181
|
+
} catch {
|
|
182
|
+
current = DEFAULT_ENGINE;
|
|
183
|
+
}
|
|
184
|
+
const rows = allEngineStatuses(probes);
|
|
185
|
+
const detail = `Default engine: ${current}. ${rows.map((row) => `${row.id} ${row.availability === "ready" ? statusText(row) : `unavailable (${row.reason})`}`).join("; ")}`;
|
|
186
|
+
const chosen = engineStatus(engineById(current), probes);
|
|
187
|
+
if (chosen.availability === "ready") return { id: "engines", ok: true, label: "Render engines", detail };
|
|
188
|
+
const other = rows.find((row) => row.availability === "ready");
|
|
189
|
+
return {
|
|
190
|
+
id: "engines",
|
|
191
|
+
ok: false,
|
|
192
|
+
label: "Render engines",
|
|
193
|
+
detail,
|
|
194
|
+
fix: `The default engine cannot render. Run \`npx vfrey engine use ${other?.id ?? "remotion"}\`.`,
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
export function runDoctor(env: Env, homedir: string = os.homedir(), cwd: string = process.cwd()): DoctorReport {
|
|
199
|
+
const home = resolveHome(env, homedir, cwd);
|
|
200
|
+
const dataDir = home.dataDir;
|
|
201
|
+
const checks = [nodeCheck(), projectCheck(home), homeCheck(dataDir), chromeCheck(env, cwd), enginesCheck(env, cwd, dataDir), voiceCheck(env), credentialsKeyCheck(env), skillCheck(env, cwd, homedir), { id: "account", ok: true, label: "Account", detail: accountLine(env, homedir) }];
|
|
202
|
+
return { ok: checks.every((check) => check.ok), dataDir, checks };
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
export function formatDoctor(report: DoctorReport): string {
|
|
206
|
+
const lines: string[] = [];
|
|
207
|
+
for (const check of report.checks) {
|
|
208
|
+
lines.push(`${check.ok ? "✓" : "✗"} ${check.id}: ${check.detail}`);
|
|
209
|
+
if (!check.ok && check.fix) lines.push(` Fix: ${check.fix}`);
|
|
210
|
+
}
|
|
211
|
+
return lines.join("\n");
|
|
212
|
+
}
|
package/src/drain.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { LEASE_MS, processJob, type Runtime } from "./pipeline/run.js";
|
|
2
|
+
|
|
3
|
+
/** Runs queued jobs until none are due or the stop flag is set. Engage and analytics use it. */
|
|
4
|
+
export async function drain(runtime: Runtime): Promise<number> {
|
|
5
|
+
let processed = 0;
|
|
6
|
+
for (;;) {
|
|
7
|
+
if (runtime.shouldStop?.()) break;
|
|
8
|
+
const job = runtime.store.claim(runtime.now(), runtime.leaseMs ?? LEASE_MS);
|
|
9
|
+
if (!job) break;
|
|
10
|
+
await processJob(runtime, job);
|
|
11
|
+
processed += 1;
|
|
12
|
+
}
|
|
13
|
+
return processed;
|
|
14
|
+
}
|