@vfrey/vfrey 0.0.1 → 1.0.0-beta.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.
Files changed (610) hide show
  1. package/LICENSE +33 -0
  2. package/README.md +78 -3
  3. package/accounts.example.json +61 -0
  4. package/accounts.first-run.json +41 -0
  5. package/bin/vfrey.mjs +36 -0
  6. package/integration-tools/README.md +43 -0
  7. package/integration-tools/image/pillow.md +32 -0
  8. package/integration-tools/image/playwright.md +33 -0
  9. package/integration-tools/image/resvg.md +36 -0
  10. package/integration-tools/image/satori.md +36 -0
  11. package/integration-tools/image/sharp.md +38 -0
  12. package/integration-tools/video/blender.md +34 -0
  13. package/integration-tools/video/ffmpeg.md +41 -0
  14. package/integration-tools/video/manim.md +37 -0
  15. package/integration-tools/video/motion-canvas.md +37 -0
  16. package/integration-tools/video/remotion.md +50 -0
  17. package/integration-tools/video/revideo.md +32 -0
  18. package/package.json +74 -6
  19. package/skills/vfrey-orchestrator/SKILL.md +15 -0
  20. package/skills/vfrey-orchestrator/channel-video/SKILL.md +41 -0
  21. package/skills/vfrey-product/SKILL.md +63 -0
  22. package/skills/vfrey-product/examples/concurrency-vs-parallelism/framey/cook-one.svg +14 -0
  23. package/skills/vfrey-product/examples/concurrency-vs-parallelism/framey/cook-two.svg +22 -0
  24. package/skills/vfrey-product/examples/concurrency-vs-parallelism/framey/index.html +70 -0
  25. package/skills/vfrey-product/examples/concurrency-vs-parallelism/remotion/cook-one.svg +14 -0
  26. package/skills/vfrey-product/examples/concurrency-vs-parallelism/remotion/cook-two.svg +22 -0
  27. package/skills/vfrey-product/examples/concurrency-vs-parallelism/remotion/index.tsx +49 -0
  28. package/skills/vfrey-product/examples/concurrency-vs-parallelism/script.json +15 -0
  29. package/skills/vfrey-product/examples/octopus-facts/index.html +60 -0
  30. package/skills/vfrey-product/examples/octopus-facts/octopus.svg +8 -0
  31. package/skills/vfrey-product/examples/octopus-facts/script.json +14 -0
  32. package/skills/vfrey-product/push/SKILL.md +15 -0
  33. package/skills/vfrey-product/scenes/NOTICE +5 -0
  34. package/skills/vfrey-product/scenes/blocks.md +57 -0
  35. package/skills/vfrey-product/scenes/craft.md +118 -0
  36. package/skills/vfrey-product/scenes/motion.md +88 -0
  37. package/src/analytics/dashboard.ts +109 -0
  38. package/src/analytics/index.ts +260 -0
  39. package/src/api/client.ts +361 -0
  40. package/src/api/cloud-files.ts +197 -0
  41. package/src/api/cloud-runs.ts +75 -0
  42. package/src/api/cloud.ts +413 -0
  43. package/src/api/commands.ts +330 -0
  44. package/src/api/config.ts +34 -0
  45. package/src/api/creators.ts +463 -0
  46. package/src/api/credentials.ts +152 -0
  47. package/src/api/mail.ts +333 -0
  48. package/src/api/topics.ts +207 -0
  49. package/src/api/types.ts +273 -0
  50. package/src/cli.ts +468 -0
  51. package/src/cloud/bundle.ts +70 -0
  52. package/src/cloud/cloudrun-executor.ts +237 -0
  53. package/src/cloud/executor.ts +281 -0
  54. package/src/cloud/gcs-sign.ts +76 -0
  55. package/src/cloud/job.ts +106 -0
  56. package/src/cloud/remote-job.ts +67 -0
  57. package/src/cloud/render-job.ts +191 -0
  58. package/src/config.ts +309 -0
  59. package/src/dashboard-link.ts +180 -0
  60. package/src/dashboard-start.ts +103 -0
  61. package/src/desk/channel.ts +64 -0
  62. package/src/desk/cli.ts +216 -0
  63. package/src/desk/creators-route.ts +129 -0
  64. package/src/desk/fonts/JetBrainsMono-OFL.txt +93 -0
  65. package/src/desk/fonts/google-sans-flex-latin.woff2 +0 -0
  66. package/src/desk/fonts/jetbrains-mono-medium.woff2 +0 -0
  67. package/src/desk/http.ts +460 -0
  68. package/src/desk/mail-route.ts +103 -0
  69. package/src/desk/overlay.ts +112 -0
  70. package/src/desk/preview.ts +422 -0
  71. package/src/desk/screens.ts +149 -0
  72. package/src/desk/secrets.ts +143 -0
  73. package/src/desk/series.ts +127 -0
  74. package/src/desk/state.ts +77 -0
  75. package/src/desk/studio-route.ts +119 -0
  76. package/src/desk/tick-runner.ts +66 -0
  77. package/src/desk/tick.ts +56 -0
  78. package/src/desk/topics-route.ts +41 -0
  79. package/src/desk/ui/app.css +505 -0
  80. package/src/desk/ui/app.js +185 -0
  81. package/src/desk/ui/apple-icon.png +0 -0
  82. package/src/desk/ui/components.js +79 -0
  83. package/src/desk/ui/core.js +156 -0
  84. package/src/desk/ui/favicon.ico +0 -0
  85. package/src/desk/ui/icon.svg +1 -0
  86. package/src/desk/ui/icons.js +49 -0
  87. package/src/desk/ui/index.html +41 -0
  88. package/src/desk/ui/metrics-math.d.ts +22 -0
  89. package/src/desk/ui/metrics-math.js +135 -0
  90. package/src/desk/ui/theme.js +5 -0
  91. package/src/desk/ui/views-creators.js +209 -0
  92. package/src/desk/ui/views-keys.js +114 -0
  93. package/src/desk/ui/views-mail.js +188 -0
  94. package/src/desk/ui/views-misc.js +123 -0
  95. package/src/desk/ui/views-review.js +119 -0
  96. package/src/desk/ui/views-runs.js +350 -0
  97. package/src/desk/ui/views-screens.js +163 -0
  98. package/src/desk/ui/views-series.js +318 -0
  99. package/src/desk/ui/views-topics.js +87 -0
  100. package/src/desk/verbs.ts +361 -0
  101. package/src/doctor.ts +212 -0
  102. package/src/drain.ts +14 -0
  103. package/src/engage/index.ts +217 -0
  104. package/src/generate/index.ts +110 -0
  105. package/src/generate/script.ts +152 -0
  106. package/src/generate/title.ts +78 -0
  107. package/src/help.ts +252 -0
  108. package/src/home.ts +45 -0
  109. package/src/llm/client.ts +177 -0
  110. package/src/log.ts +7 -0
  111. package/src/oauth/tokens.ts +189 -0
  112. package/src/oauth/vault.ts +72 -0
  113. package/src/pipeline/review.ts +642 -0
  114. package/src/pipeline/run.ts +443 -0
  115. package/src/pipeline/series-guard.ts +26 -0
  116. package/src/pipeline/source-fetch.ts +69 -0
  117. package/src/pipeline/stages.ts +728 -0
  118. package/src/pipeline/topics-source.ts +90 -0
  119. package/src/product/commands.ts +296 -0
  120. package/src/product/config-file.ts +38 -0
  121. package/src/product/engine-choice.ts +89 -0
  122. package/src/product/engines.ts +103 -0
  123. package/src/product/insights.ts +50 -0
  124. package/src/product/keys.ts +67 -0
  125. package/src/product/paths.ts +18 -0
  126. package/src/product/probes.ts +88 -0
  127. package/src/product/push-flow.ts +90 -0
  128. package/src/product/render-video.ts +21 -0
  129. package/src/product/short.ts +62 -0
  130. package/src/product/update.ts +32 -0
  131. package/src/project/canonical.ts +14 -0
  132. package/src/project/commands.ts +177 -0
  133. package/src/project/dashboard-status.ts +28 -0
  134. package/src/project/discover.ts +19 -0
  135. package/src/project/index-file.ts +73 -0
  136. package/src/project/marker.ts +92 -0
  137. package/src/project/migrate.ts +201 -0
  138. package/src/project/names.ts +27 -0
  139. package/src/project/notice.ts +16 -0
  140. package/src/project/open.ts +88 -0
  141. package/src/project/scaffold.ts +112 -0
  142. package/src/project/templates.ts +42 -0
  143. package/src/project/videos.ts +64 -0
  144. package/src/publish/facebook.ts +91 -0
  145. package/src/publish/index.ts +12 -0
  146. package/src/publish/instagram.ts +97 -0
  147. package/src/publish/rate.ts +62 -0
  148. package/src/publish/session.ts +242 -0
  149. package/src/publish/tiktok.ts +202 -0
  150. package/src/publish/youtube.ts +177 -0
  151. package/src/queue/desk-ops.ts +1170 -0
  152. package/src/queue/seal.ts +3 -0
  153. package/src/queue/store.ts +1006 -0
  154. package/src/remotion/AiLabel.tsx +26 -0
  155. package/src/remotion/Root.tsx +73 -0
  156. package/src/remotion/defaults.ts +26 -0
  157. package/src/remotion/films/claude/ClaudeFilm.tsx +1014 -0
  158. package/src/remotion/films/engine/EngineMark.tsx +17 -0
  159. package/src/remotion/films/rea/ReaShort.tsx +552 -0
  160. package/src/remotion/films/rea/drawings.tsx +907 -0
  161. package/src/remotion/films/rea/timing.ts +144 -0
  162. package/src/remotion/index.ts +4 -0
  163. package/src/remotion/templates/EditorialSplit.tsx +39 -0
  164. package/src/remotion/templates/KineticType.tsx +39 -0
  165. package/src/remotion/templates/SignalBoard.tsx +50 -0
  166. package/src/remotion/timing.ts +22 -0
  167. package/src/render/chrome.ts +78 -0
  168. package/src/render/engine-mark.ts +29 -0
  169. package/src/render/framey-template.ts +130 -0
  170. package/src/render/html-engine.ts +149 -0
  171. package/src/render/node-crypto-stub.ts +11 -0
  172. package/src/render/probe.ts +29 -0
  173. package/src/render/props.ts +17 -0
  174. package/src/render/vertical-short.ts +35 -0
  175. package/src/render/webpack.ts +33 -0
  176. package/src/render/worker.ts +65 -0
  177. package/src/scene/assets/fonts/TitanOne-OFL.txt +94 -0
  178. package/src/scene/assets/fonts/titan-one.woff2 +0 -0
  179. package/src/scene/assets/gsap/LICENSE.txt +8 -0
  180. package/src/scene/assets/gsap/gsap.min.js +11 -0
  181. package/src/scene/audio.ts +54 -0
  182. package/src/scene/blocks/LICENSE +190 -0
  183. package/src/scene/blocks/NOTICE +5 -0
  184. package/src/scene/blocks/bar-chart-race/bar-chart-race.html +585 -0
  185. package/src/scene/blocks/beat-freeze-cut/beat-freeze-cut.html +874 -0
  186. package/src/scene/blocks/camcorder-hud/camcorder-hud.html +164 -0
  187. package/src/scene/blocks/camera-dolly-zoom/camera-dolly-zoom.html +355 -0
  188. package/src/scene/blocks/code-diff/code-diff.html +817 -0
  189. package/src/scene/blocks/code-snippet-apple-terminal-pro/code-snippet-apple-terminal-pro.html +248 -0
  190. package/src/scene/blocks/code-typing/code-typing.html +764 -0
  191. package/src/scene/blocks/data-chart/data-chart.html +452 -0
  192. package/src/scene/blocks/flowchart-vertical/flowchart-vertical.html +454 -0
  193. package/src/scene/blocks/hw-scribble-transition/hw-scribble-transition.html +153 -0
  194. package/src/scene/blocks/index.json +824 -0
  195. package/src/scene/blocks/lt-accent-underline/lt-accent-underline.html +107 -0
  196. package/src/scene/blocks/lt-bold-block/lt-bold-block.html +109 -0
  197. package/src/scene/blocks/lt-clean-bar/lt-clean-bar.html +112 -0
  198. package/src/scene/blocks/lt-dark-card/lt-dark-card.html +111 -0
  199. package/src/scene/blocks/lt-kicker-name/lt-kicker-name.html +114 -0
  200. package/src/scene/blocks/lt-mask-reveal/lt-mask-reveal.html +120 -0
  201. package/src/scene/blocks/lt-neon-border/lt-neon-border.html +391 -0
  202. package/src/scene/blocks/lt-stack-bars/lt-stack-bars.html +109 -0
  203. package/src/scene/blocks/macos-notification/macos-notification.html +194 -0
  204. package/src/scene/blocks/mk-background/mk-background.html +236 -0
  205. package/src/scene/blocks/mk-callout-highlight/mk-callout-highlight.html +183 -0
  206. package/src/scene/blocks/mk-clone-wall-transition/mk-clone-wall-transition.html +226 -0
  207. package/src/scene/blocks/mk-line-graph/mk-line-graph.html +293 -0
  208. package/src/scene/blocks/mk-progress-stat/mk-progress-stat.html +186 -0
  209. package/src/scene/blocks/mk-specs-list/mk-specs-list.html +223 -0
  210. package/src/scene/blocks/news-ticker/news-ticker.html +409 -0
  211. package/src/scene/blocks/organic-light-leak-overlay/organic-light-leak-overlay.html +126 -0
  212. package/src/scene/blocks/oscilloscope-trace/oscilloscope-trace.html +519 -0
  213. package/src/scene/blocks/snow/snow.html +205 -0
  214. package/src/scene/blocks/split-flap-board/split-flap-board.html +347 -0
  215. package/src/scene/blocks/spotify-card/spotify-card.html +294 -0
  216. package/src/scene/blocks/starfield/starfield.html +188 -0
  217. package/src/scene/blocks/thread-message-stack/thread-message-stack.html +239 -0
  218. package/src/scene/blocks/week-in-merges/week-in-merges.html +470 -0
  219. package/src/scene/blocks/x-post/x-post.html +437 -0
  220. package/src/scene/blocks/yt-comment-card/yt-comment-card.html +319 -0
  221. package/src/scene/blocks/yt-lcd-background/yt-lcd-background.html +307 -0
  222. package/src/scene/blocks/yt-prism-title/yt-prism-title.html +205 -0
  223. package/src/scene/blocks.ts +229 -0
  224. package/src/scene/command.ts +272 -0
  225. package/src/scene/figure.ts +181 -0
  226. package/src/scene/glyphs.ts +107 -0
  227. package/src/scene/init.ts +86 -0
  228. package/src/scene/inspect/css-tree.d.ts +6 -0
  229. package/src/scene/inspect/page-scripts.ts +272 -0
  230. package/src/scene/inspect/report.ts +405 -0
  231. package/src/scene/inspect/run.ts +200 -0
  232. package/src/scene/inspect/source.ts +236 -0
  233. package/src/scene/label.ts +131 -0
  234. package/src/scene/net-guard.ts +74 -0
  235. package/src/scene/render.ts +292 -0
  236. package/src/scene/runtime/browser.ts +199 -0
  237. package/src/scene/runtime/core.ts +181 -0
  238. package/src/scene/runtime/react.tsx +115 -0
  239. package/src/scene/runtime/transitions.ts +133 -0
  240. package/src/scene/sanitize.ts +181 -0
  241. package/src/scene/scene.ts +152 -0
  242. package/src/scene/studio-command.ts +48 -0
  243. package/src/scene/studio-skin.ts +59 -0
  244. package/src/scene/studio.ts +337 -0
  245. package/src/scene/supply.ts +111 -0
  246. package/src/scene/timing.ts +288 -0
  247. package/src/scene/transitions/LICENSE +190 -0
  248. package/src/scene/transitions/NOTICE +5 -0
  249. package/src/scene/transitions/SOURCE.json +24 -0
  250. package/src/scene/transitions/transitions.global.js +38 -0
  251. package/src/scene/transitions.ts +26 -0
  252. package/src/service/all-projects.ts +153 -0
  253. package/src/service/commands.ts +256 -0
  254. package/src/service/due.ts +82 -0
  255. package/src/service/lock.ts +101 -0
  256. package/src/service/log-file.ts +56 -0
  257. package/src/service/state.ts +41 -0
  258. package/src/service/units.ts +90 -0
  259. package/src/service/worker.ts +51 -0
  260. package/src/skill-agents.ts +74 -0
  261. package/src/skill-install.ts +334 -0
  262. package/src/sync-write.ts +21 -0
  263. package/src/tick-report.ts +205 -0
  264. package/src/tts.ts +124 -0
  265. package/vendor/@vfrey/contracts/dist/account.d.ts +97 -0
  266. package/vendor/@vfrey/contracts/dist/account.js +27 -0
  267. package/vendor/@vfrey/contracts/dist/checks.d.ts +8 -0
  268. package/vendor/@vfrey/contracts/dist/checks.js +1 -0
  269. package/vendor/@vfrey/contracts/dist/credentials/crypto.d.ts +4 -0
  270. package/vendor/@vfrey/contracts/dist/credentials/crypto.js +38 -0
  271. package/vendor/@vfrey/contracts/dist/domain/advice-persona.d.ts +10 -0
  272. package/vendor/@vfrey/contracts/dist/domain/advice-persona.js +24 -0
  273. package/vendor/@vfrey/contracts/dist/domain/backoff.d.ts +13 -0
  274. package/vendor/@vfrey/contracts/dist/domain/backoff.js +27 -0
  275. package/vendor/@vfrey/contracts/dist/domain/cadence.d.ts +29 -0
  276. package/vendor/@vfrey/contracts/dist/domain/cadence.js +95 -0
  277. package/vendor/@vfrey/contracts/dist/domain/creators.d.ts +146 -0
  278. package/vendor/@vfrey/contracts/dist/domain/creators.js +2 -0
  279. package/vendor/@vfrey/contracts/dist/domain/disclosure.d.ts +127 -0
  280. package/vendor/@vfrey/contracts/dist/domain/disclosure.js +250 -0
  281. package/vendor/@vfrey/contracts/dist/domain/errors.d.ts +34 -0
  282. package/vendor/@vfrey/contracts/dist/domain/errors.js +68 -0
  283. package/vendor/@vfrey/contracts/dist/domain/redact.d.ts +1 -0
  284. package/vendor/@vfrey/contracts/dist/domain/redact.js +21 -0
  285. package/vendor/@vfrey/contracts/dist/domain/revenue.d.ts +13 -0
  286. package/vendor/@vfrey/contracts/dist/domain/revenue.js +16 -0
  287. package/vendor/@vfrey/contracts/dist/domain/safety.d.ts +20 -0
  288. package/vendor/@vfrey/contracts/dist/domain/safety.js +61 -0
  289. package/vendor/@vfrey/contracts/dist/domain/similarity.d.ts +13 -0
  290. package/vendor/@vfrey/contracts/dist/domain/similarity.js +100 -0
  291. package/vendor/@vfrey/contracts/dist/domain/templates.d.ts +18 -0
  292. package/vendor/@vfrey/contracts/dist/domain/templates.js +67 -0
  293. package/vendor/@vfrey/contracts/dist/domain/tiktok-chunks.d.ts +19 -0
  294. package/vendor/@vfrey/contracts/dist/domain/tiktok-chunks.js +52 -0
  295. package/vendor/@vfrey/contracts/dist/domain/topics.d.ts +78 -0
  296. package/vendor/@vfrey/contracts/dist/domain/topics.js +4 -0
  297. package/vendor/@vfrey/contracts/dist/domain/types.d.ts +216 -0
  298. package/vendor/@vfrey/contracts/dist/domain/types.js +1 -0
  299. package/vendor/@vfrey/contracts/dist/domain/variation.d.ts +57 -0
  300. package/vendor/@vfrey/contracts/dist/domain/variation.js +278 -0
  301. package/vendor/@vfrey/contracts/dist/events.d.ts +20 -0
  302. package/vendor/@vfrey/contracts/dist/events.js +1 -0
  303. package/vendor/@vfrey/contracts/dist/http.d.ts +11 -0
  304. package/vendor/@vfrey/contracts/dist/http.js +37 -0
  305. package/vendor/@vfrey/contracts/dist/index.d.ts +22 -0
  306. package/vendor/@vfrey/contracts/dist/index.js +22 -0
  307. package/vendor/@vfrey/contracts/dist/log.d.ts +2 -0
  308. package/vendor/@vfrey/contracts/dist/log.js +10 -0
  309. package/vendor/@vfrey/contracts/dist/mail.d.ts +55 -0
  310. package/vendor/@vfrey/contracts/dist/mail.js +1 -0
  311. package/vendor/@vfrey/contracts/package.json +18 -0
  312. package/vendor/@vfrey/ingest/dist/index.d.ts +41 -0
  313. package/vendor/@vfrey/ingest/dist/index.js +221 -0
  314. package/vendor/@vfrey/ingest/dist/parse.d.ts +5 -0
  315. package/vendor/@vfrey/ingest/dist/parse.js +165 -0
  316. package/vendor/@vfrey/ingest/package.json +21 -0
  317. package/vendor/framey/LICENSE +190 -0
  318. package/vendor/framey/NOTICE +15 -0
  319. package/vendor/framey/SOURCE.json +24 -0
  320. package/vendor/framey/STUDIO-SOURCE.json +9 -0
  321. package/vendor/framey/bin/framey-localize-fonts.mjs +12 -0
  322. package/vendor/framey/bin/framey.mjs +16 -0
  323. package/vendor/framey/dist/add-VMJD5KBS.js +75 -0
  324. package/vendor/framey/dist/animatedGifPrep-QPKGJ6PM.js +43 -0
  325. package/vendor/framey/dist/api.d.ts +211 -0
  326. package/vendor/framey/dist/api.js +76 -0
  327. package/vendor/framey/dist/appHistory-WW56FZTT.js +31 -0
  328. package/vendor/framey/dist/asset-WQKGOELE.js +26 -0
  329. package/vendor/framey/dist/audio-RSJWVWRA.js +1936 -0
  330. package/vendor/framey/dist/batchRender-3HPICDCT.js +366 -0
  331. package/vendor/framey/dist/beats-HL2BDUZI.js +273 -0
  332. package/vendor/framey/dist/benchmark-RWCWHI5Z.js +230 -0
  333. package/vendor/framey/dist/browser-ECYET3Q2.js +206 -0
  334. package/vendor/framey/dist/bundleWithLocalizedFonts-ALYBQEQT.js +52 -0
  335. package/vendor/framey/dist/captureCompositionFrame-4DA6SEH2.js +94 -0
  336. package/vendor/framey/dist/catalog-5JVDEUWK.js +979 -0
  337. package/vendor/framey/dist/catch-up-NEBCL5NO.js +106 -0
  338. package/vendor/framey/dist/check-F5N544ML.js +111 -0
  339. package/vendor/framey/dist/checkBrowser-LZ66CMJG.js +110 -0
  340. package/vendor/framey/dist/chunk-2K7Z2HVP.js +372 -0
  341. package/vendor/framey/dist/chunk-2UZQHDM7.js +323 -0
  342. package/vendor/framey/dist/chunk-2X4PQEYM.js +34 -0
  343. package/vendor/framey/dist/chunk-3422KMTD.js +168 -0
  344. package/vendor/framey/dist/chunk-3FYD4YEK.js +72 -0
  345. package/vendor/framey/dist/chunk-3GXQPCJC.js +682 -0
  346. package/vendor/framey/dist/chunk-3LM6YPBR.js +179 -0
  347. package/vendor/framey/dist/chunk-442JNXRM.js +20 -0
  348. package/vendor/framey/dist/chunk-4BNN5FGX.js +182 -0
  349. package/vendor/framey/dist/chunk-4P7NX5BM.js +11532 -0
  350. package/vendor/framey/dist/chunk-4UMPIPCQ.js +88 -0
  351. package/vendor/framey/dist/chunk-4YYJOFKS.js +28 -0
  352. package/vendor/framey/dist/chunk-54HJ63Q2.js +29 -0
  353. package/vendor/framey/dist/chunk-5ETJX6WM.js +89 -0
  354. package/vendor/framey/dist/chunk-5SG54JRS.js +68 -0
  355. package/vendor/framey/dist/chunk-634HSJ3J.js +58 -0
  356. package/vendor/framey/dist/chunk-66LREXGS.js +48 -0
  357. package/vendor/framey/dist/chunk-6BEFEWFB.js +25 -0
  358. package/vendor/framey/dist/chunk-6BNTGNGD.js +286 -0
  359. package/vendor/framey/dist/chunk-6DDPOHMK.js +140 -0
  360. package/vendor/framey/dist/chunk-6GV5XSOR.js +214 -0
  361. package/vendor/framey/dist/chunk-6JR2XUEE.js +127 -0
  362. package/vendor/framey/dist/chunk-73QVATIM.js +362 -0
  363. package/vendor/framey/dist/chunk-7FMUTGOH.js +279 -0
  364. package/vendor/framey/dist/chunk-7JQFJ6JJ.js +291 -0
  365. package/vendor/framey/dist/chunk-AHPGAESX.js +113 -0
  366. package/vendor/framey/dist/chunk-AHX34EFP.js +15 -0
  367. package/vendor/framey/dist/chunk-AJ5CSAEZ.js +487 -0
  368. package/vendor/framey/dist/chunk-B7RJV74O.js +5047 -0
  369. package/vendor/framey/dist/chunk-BL4U6U52.js +254 -0
  370. package/vendor/framey/dist/chunk-BSQ7XANM.js +210 -0
  371. package/vendor/framey/dist/chunk-BZ56MTDX.js +6 -0
  372. package/vendor/framey/dist/chunk-C5BO7PMT.js +282 -0
  373. package/vendor/framey/dist/chunk-CFUCHO4T.js +358 -0
  374. package/vendor/framey/dist/chunk-CZGHFATT.js +31 -0
  375. package/vendor/framey/dist/chunk-DLA44CMS.js +5649 -0
  376. package/vendor/framey/dist/chunk-DPVXBVLL.js +31292 -0
  377. package/vendor/framey/dist/chunk-DQBY7FRR.js +397 -0
  378. package/vendor/framey/dist/chunk-EAMLBAHA.js +154 -0
  379. package/vendor/framey/dist/chunk-EOVYQ3IG.js +143 -0
  380. package/vendor/framey/dist/chunk-EVMY7IPL.js +612 -0
  381. package/vendor/framey/dist/chunk-FG4BBFZI.js +122 -0
  382. package/vendor/framey/dist/chunk-FI4I7CME.js +56 -0
  383. package/vendor/framey/dist/chunk-FLREUEHW.js +2081 -0
  384. package/vendor/framey/dist/chunk-FUFP7A7S.js +1126 -0
  385. package/vendor/framey/dist/chunk-FV3PIKGH.js +56 -0
  386. package/vendor/framey/dist/chunk-FXMKHHPO.js +123 -0
  387. package/vendor/framey/dist/chunk-GGZAM5VH.js +1187 -0
  388. package/vendor/framey/dist/chunk-GKZEVJIB.js +39 -0
  389. package/vendor/framey/dist/chunk-GMECXFAN.js +57 -0
  390. package/vendor/framey/dist/chunk-GNEFDLFB.js +621 -0
  391. package/vendor/framey/dist/chunk-GPMLSOEG.js +19274 -0
  392. package/vendor/framey/dist/chunk-GPYVT5EE.js +59 -0
  393. package/vendor/framey/dist/chunk-H6IEMZEV.js +40 -0
  394. package/vendor/framey/dist/chunk-HEFQCIXR.js +170 -0
  395. package/vendor/framey/dist/chunk-HFQ4CWHA.js +118 -0
  396. package/vendor/framey/dist/chunk-HPDTCOKE.js +304 -0
  397. package/vendor/framey/dist/chunk-HTUHQKDB.js +548 -0
  398. package/vendor/framey/dist/chunk-HU2SPFNG.js +103 -0
  399. package/vendor/framey/dist/chunk-HVW7ABXD.js +19 -0
  400. package/vendor/framey/dist/chunk-IG7NDKXO.js +26 -0
  401. package/vendor/framey/dist/chunk-II3J3WXK.js +246 -0
  402. package/vendor/framey/dist/chunk-IR35BCLK.js +13 -0
  403. package/vendor/framey/dist/chunk-ISN5GRYI.js +20 -0
  404. package/vendor/framey/dist/chunk-IUONB55J.js +669 -0
  405. package/vendor/framey/dist/chunk-IVSUQR2B.js +161 -0
  406. package/vendor/framey/dist/chunk-IXU6PQSW.js +259 -0
  407. package/vendor/framey/dist/chunk-IZCW2NKT.js +29 -0
  408. package/vendor/framey/dist/chunk-JGXJU5PF.js +68 -0
  409. package/vendor/framey/dist/chunk-JHOCK3DG.js +27 -0
  410. package/vendor/framey/dist/chunk-JVBRJMOP.js +121 -0
  411. package/vendor/framey/dist/chunk-K4MJX7SR.js +17 -0
  412. package/vendor/framey/dist/chunk-K4WKGXRC.js +18 -0
  413. package/vendor/framey/dist/chunk-KGPLIH7B.js +127 -0
  414. package/vendor/framey/dist/chunk-KLIKCX6Q.js +63 -0
  415. package/vendor/framey/dist/chunk-KXTKKBOC.js +61 -0
  416. package/vendor/framey/dist/chunk-KZI44OPA.js +185 -0
  417. package/vendor/framey/dist/chunk-LEKZNRKI.js +45 -0
  418. package/vendor/framey/dist/chunk-LFGJWOVA.js +61 -0
  419. package/vendor/framey/dist/chunk-LLMSX6V6.js +17 -0
  420. package/vendor/framey/dist/chunk-LTGTN2TH.js +195 -0
  421. package/vendor/framey/dist/chunk-M2WBOQ2Q.js +54 -0
  422. package/vendor/framey/dist/chunk-MI5FTLYK.js +399 -0
  423. package/vendor/framey/dist/chunk-MLQP2QUF.js +1809 -0
  424. package/vendor/framey/dist/chunk-MMQ52NIZ.js +956 -0
  425. package/vendor/framey/dist/chunk-ND46XKK3.js +742 -0
  426. package/vendor/framey/dist/chunk-NYEXNSHM.js +504 -0
  427. package/vendor/framey/dist/chunk-O4YRB27L.js +57 -0
  428. package/vendor/framey/dist/chunk-O7BGSQ5F.js +92 -0
  429. package/vendor/framey/dist/chunk-OFQWTKPW.js +117 -0
  430. package/vendor/framey/dist/chunk-P5ZBLXJ6.js +91 -0
  431. package/vendor/framey/dist/chunk-P6LJ4Y32.js +4662 -0
  432. package/vendor/framey/dist/chunk-PKZ7RK5N.js +281 -0
  433. package/vendor/framey/dist/chunk-PURHAXFL.js +15 -0
  434. package/vendor/framey/dist/chunk-Q5X2B5WT.js +29 -0
  435. package/vendor/framey/dist/chunk-QAMKOG67.js +248 -0
  436. package/vendor/framey/dist/chunk-R6YBK4XX.js +514 -0
  437. package/vendor/framey/dist/chunk-RGLQURIX.js +2997 -0
  438. package/vendor/framey/dist/chunk-RNV3DM4S.js +3573 -0
  439. package/vendor/framey/dist/chunk-ROEW726S.js +4638 -0
  440. package/vendor/framey/dist/chunk-RQVDTPGD.js +6 -0
  441. package/vendor/framey/dist/chunk-RVZHGOGR.js +26 -0
  442. package/vendor/framey/dist/chunk-RXATE6FZ.js +385 -0
  443. package/vendor/framey/dist/chunk-S3S4TGEH.js +424 -0
  444. package/vendor/framey/dist/chunk-SDYDNHEE.js +11202 -0
  445. package/vendor/framey/dist/chunk-SSWBU67I.js +173 -0
  446. package/vendor/framey/dist/chunk-ST35C4K6.js +28 -0
  447. package/vendor/framey/dist/chunk-SZNTYTIV.js +990 -0
  448. package/vendor/framey/dist/chunk-T2EUWCJE.js +29 -0
  449. package/vendor/framey/dist/chunk-T6AGUTZJ.js +444 -0
  450. package/vendor/framey/dist/chunk-THJALHFJ.js +11131 -0
  451. package/vendor/framey/dist/chunk-TIQB63MX.js +18128 -0
  452. package/vendor/framey/dist/chunk-U242RTCT.js +134 -0
  453. package/vendor/framey/dist/chunk-UJZREAV6.js +3901 -0
  454. package/vendor/framey/dist/chunk-UMD3V7CU.js +264 -0
  455. package/vendor/framey/dist/chunk-UOFAE5RF.js +42 -0
  456. package/vendor/framey/dist/chunk-UTEIUGAZ.js +29 -0
  457. package/vendor/framey/dist/chunk-VAS6HQX3.js +138 -0
  458. package/vendor/framey/dist/chunk-VC76R6W2.js +27 -0
  459. package/vendor/framey/dist/chunk-VFZ7W5W7.js +226 -0
  460. package/vendor/framey/dist/chunk-VHNLZAE2.js +348 -0
  461. package/vendor/framey/dist/chunk-VMC4WSH5.js +278 -0
  462. package/vendor/framey/dist/chunk-VORQIH64.js +108 -0
  463. package/vendor/framey/dist/chunk-VTXFRIHO.js +186 -0
  464. package/vendor/framey/dist/chunk-W2LGJSEN.js +182 -0
  465. package/vendor/framey/dist/chunk-WKBM7NDG.js +32 -0
  466. package/vendor/framey/dist/chunk-WMQNIQDJ.js +2057 -0
  467. package/vendor/framey/dist/chunk-WN4XZC5F.js +504 -0
  468. package/vendor/framey/dist/chunk-WQ6ZXHFQ.js +133 -0
  469. package/vendor/framey/dist/chunk-X7HBSTCZ.js +53 -0
  470. package/vendor/framey/dist/chunk-X7MTZ4T2.js +6 -0
  471. package/vendor/framey/dist/chunk-XPTOJZJF.js +308 -0
  472. package/vendor/framey/dist/chunk-XT6MXC7A.js +6 -0
  473. package/vendor/framey/dist/chunk-XT7X6QCP.js +6 -0
  474. package/vendor/framey/dist/chunk-XTMDWNEC.js +2820 -0
  475. package/vendor/framey/dist/chunk-XUHOIDKG.js +6 -0
  476. package/vendor/framey/dist/chunk-XX7LTW5Y.js +138 -0
  477. package/vendor/framey/dist/chunk-Y3YGTZEG.js +382 -0
  478. package/vendor/framey/dist/chunk-Y5SBSGN7.js +346 -0
  479. package/vendor/framey/dist/chunk-YCMVI4MZ.js +625 -0
  480. package/vendor/framey/dist/chunk-YOR4CTCE.js +261 -0
  481. package/vendor/framey/dist/chunk-YPRBGITP.js +2748 -0
  482. package/vendor/framey/dist/chunk-ZKDMAOFU.js +32 -0
  483. package/vendor/framey/dist/chunk-ZW6DURPF.js +1318 -0
  484. package/vendor/framey/dist/clean-ZJOGDKDM.js +336 -0
  485. package/vendor/framey/dist/cli.js +439 -0
  486. package/vendor/framey/dist/cloudrun-HGJ3B6ZV.js +767 -0
  487. package/vendor/framey/dist/commands/contrast-audit.browser.js +643 -0
  488. package/vendor/framey/dist/commands/layout-audit.browser.js +2914 -0
  489. package/vendor/framey/dist/commands/motion-sample.browser.js +74 -0
  490. package/vendor/framey/dist/commands/motion-signature.browser.js +475 -0
  491. package/vendor/framey/dist/compare-W7UY3BAZ.js +463 -0
  492. package/vendor/framey/dist/compiler-TM3W6GYF.js +136 -0
  493. package/vendor/framey/dist/component-QF5VGYMU.js +176 -0
  494. package/vendor/framey/dist/compositions-XT3PMNDF.js +238 -0
  495. package/vendor/framey/dist/contactSheet-7W6GYHBQ.js +24 -0
  496. package/vendor/framey/dist/deploy-6FJPWKF2.js +299 -0
  497. package/vendor/framey/dist/destroy-HE2RIPKZ.js +57 -0
  498. package/vendor/framey/dist/deterministicFonts-RADD3BSX.js +50 -0
  499. package/vendor/framey/dist/dist-4S2FGGVY.js +422 -0
  500. package/vendor/framey/dist/dist-BE4B2EZE.js +32 -0
  501. package/vendor/framey/dist/dist-EWXJGN63.js +129 -0
  502. package/vendor/framey/dist/docs-VKIRFEEX.js +131 -0
  503. package/vendor/framey/dist/doctor-2UNJ2G7U.js +74 -0
  504. package/vendor/framey/dist/dom-37K6YOH4.js +14 -0
  505. package/vendor/framey/dist/dotEnv-7PZQTVIM.js +54 -0
  506. package/vendor/framey/dist/events-ACY3P2PA.js +76 -0
  507. package/vendor/framey/dist/events-IHB3B2MK.js +69 -0
  508. package/vendor/framey/dist/ffmpeg-DR5LGGSK.js +63 -0
  509. package/vendor/framey/dist/figma-BODVUPHB.js +63 -0
  510. package/vendor/framey/dist/fontData.generated-NXAOI4GH.js +13 -0
  511. package/vendor/framey/dist/fontLocalizeCli.js +142 -0
  512. package/vendor/framey/dist/gpuPolicy-XLEXORZY.js +49 -0
  513. package/vendor/framey/dist/grade-compare-GAXFQ74C.js +661 -0
  514. package/vendor/framey/dist/gsapParser-IEUSLYDJ.js +2885 -0
  515. package/vendor/framey/dist/gsapParserAcorn-LZJKCFQI.js +30 -0
  516. package/vendor/framey/dist/help-VCB5X2HS.js +184 -0
  517. package/vendor/framey/dist/history-ENGZEVFT.js +471 -0
  518. package/vendor/framey/dist/hyperframe.manifest.json +22 -0
  519. package/vendor/framey/dist/hyperframe.runtime.iife.js +1132 -0
  520. package/vendor/framey/dist/hyperframes-player.global.js +478 -0
  521. package/vendor/framey/dist/hyperframes-slideshow.global.js +148 -0
  522. package/vendor/framey/dist/info-WPCJHBMU.js +182 -0
  523. package/vendor/framey/dist/init-G4WGLYGD.js +1176 -0
  524. package/vendor/framey/dist/inspect-5IQ6CNEQ.js +106 -0
  525. package/vendor/framey/dist/installer-ML4HWUV7.js +40 -0
  526. package/vendor/framey/dist/keyframes-Q6XJO7VW.js +2755 -0
  527. package/vendor/framey/dist/lambda-A7JC74JF.js +468 -0
  528. package/vendor/framey/dist/layout-YET6XNXA.js +95 -0
  529. package/vendor/framey/dist/lint-I7FZN2VJ.js +162 -0
  530. package/vendor/framey/dist/lintProject-PDB3UN72.js +30 -0
  531. package/vendor/framey/dist/manager-CD6LJ7ZN.js +45 -0
  532. package/vendor/framey/dist/manager-WA4F2GST.js +67 -0
  533. package/vendor/framey/dist/media-treatment-BXYS4WVA.js +1030 -0
  534. package/vendor/framey/dist/media-use-LWH3MANK.js +113 -0
  535. package/vendor/framey/dist/models-K363FYM4.js +195 -0
  536. package/vendor/framey/dist/motionShot-GPAVWXLH.js +640 -0
  537. package/vendor/framey/dist/normalize-FASIPBUM.js +24 -0
  538. package/vendor/framey/dist/normalize-audio-VDWYRHN6.js +268 -0
  539. package/vendor/framey/dist/open-E3TB6P6J.js +128 -0
  540. package/vendor/framey/dist/parakeetRunner-UP34UOI2.js +58 -0
  541. package/vendor/framey/dist/pipeline-VXIUYEBU.js +64 -0
  542. package/vendor/framey/dist/play-BUTDQ4DV.js +353 -0
  543. package/vendor/framey/dist/policies-OLDYGFK5.js +309 -0
  544. package/vendor/framey/dist/preflight-2KZ2GWAZ.js +55 -0
  545. package/vendor/framey/dist/present-GXYSM7NX.js +353 -0
  546. package/vendor/framey/dist/preview-EKZ33JOJ.js +1910 -0
  547. package/vendor/framey/dist/producer-SKU5EV3J.js +13 -0
  548. package/vendor/framey/dist/progress-ZWB4Z4ET.js +76 -0
  549. package/vendor/framey/dist/projectConfig-3MZAYVTS.js +60 -0
  550. package/vendor/framey/dist/publication-DTKWNF74.js +33 -0
  551. package/vendor/framey/dist/remove-background-OY5L7XN2.js +265 -0
  552. package/vendor/framey/dist/render-CDV4GDFX.js +187 -0
  553. package/vendor/framey/dist/render-YE5YAMOD.js +2620 -0
  554. package/vendor/framey/dist/render-batch-ZAMFPCLL.js +274 -0
  555. package/vendor/framey/dist/renderSetupWorker.js +120 -0
  556. package/vendor/framey/dist/resolver-D5BIPSEW.js +42 -0
  557. package/vendor/framey/dist/runtimeVersion.js +18 -0
  558. package/vendor/framey/dist/sam-WFZXI4F7.js +23 -0
  559. package/vendor/framey/dist/shaderTransitionWorker.js +44 -0
  560. package/vendor/framey/dist/sherpa-RQWLV7I3.js +79 -0
  561. package/vendor/framey/dist/sherpaWorker.js +84 -0
  562. package/vendor/framey/dist/sites-AMOVABTK.js +54 -0
  563. package/vendor/framey/dist/skill-ZHVLH7KX.js +15 -0
  564. package/vendor/framey/dist/slideshow-SJGK4RQS.js +24 -0
  565. package/vendor/framey/dist/snapshot-TUXL6LBQ.js +1515 -0
  566. package/vendor/framey/dist/src-JHQGB2S6.js +215 -0
  567. package/vendor/framey/dist/src-Z5PGEMXB.js +532 -0
  568. package/vendor/framey/dist/state-B6H3WHCF.js +28 -0
  569. package/vendor/framey/dist/staticProjectServer-DFVZOUGI.js +63 -0
  570. package/vendor/framey/dist/studio/assets/hyperframes-player-DLaKjw0L.js +478 -0
  571. package/vendor/framey/dist/studio/assets/index-BX3KHhGX.js +71 -0
  572. package/vendor/framey/dist/studio/assets/index-CYbvOVp-.css +1 -0
  573. package/vendor/framey/dist/studio/assets/index-Dp3QEUGL.js +402 -0
  574. package/vendor/framey/dist/studio/assets/index-qrTAXbqc.js +531 -0
  575. package/vendor/framey/dist/studio/favicon.svg +14 -0
  576. package/vendor/framey/dist/studio/icons/timeline/audio.svg +7 -0
  577. package/vendor/framey/dist/studio/icons/timeline/captions.svg +5 -0
  578. package/vendor/framey/dist/studio/icons/timeline/composition.svg +12 -0
  579. package/vendor/framey/dist/studio/icons/timeline/image.svg +18 -0
  580. package/vendor/framey/dist/studio/icons/timeline/music.svg +10 -0
  581. package/vendor/framey/dist/studio/icons/timeline/text.svg +3 -0
  582. package/vendor/framey/dist/studio/index.html +23 -0
  583. package/vendor/framey/dist/studio-api-2NECGRMB.js +179 -0
  584. package/vendor/framey/dist/studioSelectionClient-IUUPKUHZ.js +25 -0
  585. package/vendor/framey/dist/studioServer-N2OPUMOQ.js +1569 -0
  586. package/vendor/framey/dist/synthesize-PKTB5ATG.js +183 -0
  587. package/vendor/framey/dist/system-7F3UEHQT.js +27 -0
  588. package/vendor/framey/dist/telemetry-DQGPEKM6.js +145 -0
  589. package/vendor/framey/dist/telemetry-UN55N6U6.js +77 -0
  590. package/vendor/framey/dist/timeline-LHUUMK63.js +1295 -0
  591. package/vendor/framey/dist/tokens-OLECWDFK.js +92 -0
  592. package/vendor/framey/dist/transcribe-O7HQKMIV.js +331 -0
  593. package/vendor/framey/dist/transcribe-UJESPBT5.js +76 -0
  594. package/vendor/framey/dist/tts-YCCQHZD4.js +234 -0
  595. package/vendor/framey/dist/urlDownloader-H2LXLG4U.js +48 -0
  596. package/vendor/framey/dist/usage-HG4WMM7A.js +2167 -0
  597. package/vendor/framey/dist/validate-F2XMN6EV.js +87 -0
  598. package/vendor/framey/dist/vendor/npm/gsap@3.15.0/GSAP-LICENSE.txt +8 -0
  599. package/vendor/framey/dist/vendor/npm/gsap@3.15.0/dist/CustomEase.min.js +11 -0
  600. package/vendor/framey/dist/vendor/npm/gsap@3.15.0/dist/MotionPathPlugin.min.js +11 -0
  601. package/vendor/framey/dist/vendor/npm/gsap@3.15.0/dist/gsap.min.js +11 -0
  602. package/vendor/framey/dist/warmCheck.js +220 -0
  603. package/vendor/framey/dist/warmCheckClient.js +139 -0
  604. package/vendor/framey/dist/workspaceBuildCheck-XYD2YTFW.js +133 -0
  605. package/vendor/framey-player/LICENSE +190 -0
  606. package/vendor/framey-player/NOTICE +16 -0
  607. package/vendor/framey-player/SOURCE.json +6 -0
  608. package/vendor/framey-player/hyperframes-player.global.js +478 -0
  609. package/vendor/framey.pin +1 -0
  610. package/bin/index.js +0 -3
@@ -0,0 +1,443 @@
1
+ import path from "node:path";
2
+ import { fileURLToPath } from "node:url";
3
+ import { AppError, ComplianceError, QualityError, RetryableError, asAppError } from "@vfrey/contracts";
4
+ import { localDayKey } from "@vfrey/contracts";
5
+ import { cadenceDecision } from "@vfrey/contracts";
6
+ import { backoffMs } from "@vfrey/contracts";
7
+ import type { Account, AppConfig, Candidate, ContentPackage } from "@vfrey/contracts";
8
+ import type { LlmClient } from "../llm/client.js";
9
+ import { log } from "../log.js";
10
+ import { applyDesk } from "../desk/overlay.js";
11
+ import { configWithSecrets, secretEnvFor } from "../desk/secrets.js";
12
+ import { deskAccounts, deskIsEmpty, seedDesk } from "../desk/series.js";
13
+ import { LeaseLost, emptyPayload, type Store, type JobRecord } from "../queue/store.js";
14
+ import { fingerprint } from "@vfrey/ingest";
15
+ import { toRenderProps } from "../render/props.js";
16
+ import type { ScriptFile } from "../generate/script.js";
17
+ import type { EngineId, EngineProbes } from "../product/engines.js";
18
+ import { retimeToVoice } from "../render/framey-template.js";
19
+ import { forcedFailure, renderHtmlEngine, voiceSeconds } from "../render/html-engine.js";
20
+ import { usableSeries } from "./series-guard.js";
21
+ import { StopRequested, noPlatformKeys, runAnalytics, runEngage, stageGenerate, stagePublish, stageRender, stageSource } from "./stages.js";
22
+
23
+ type Env = Record<string, string | undefined>;
24
+
25
+ /** What a run records about its picture: an agent-written scene (kept with the run) or the default template. */
26
+ export interface SceneRecord {
27
+ source: "agent" | "default";
28
+ /** vfrey's copy of the scene folder, `$VFREY_HOME/scenes/<job id>`. */
29
+ dir: string | null;
30
+ kind: "html" | "tsx" | null;
31
+ bytes: number;
32
+ files: number;
33
+ }
34
+
35
+ /** What the render stage hands the renderer when the run has an agent-written scene. */
36
+ export interface SceneRenderContext {
37
+ dir: string;
38
+ warn: (message: string) => void;
39
+ }
40
+
41
+ import type { Project } from "./../project/marker.js";
42
+
43
+ export interface Runtime {
44
+ config: AppConfig;
45
+ env: Env;
46
+ store: Store;
47
+ llm: LlmClient;
48
+ fetchImpl: typeof fetch;
49
+ now: () => Date;
50
+ dryRun: boolean;
51
+ skipRender: boolean;
52
+ /** The person asked for a video now. Ignore the posting window for this tick. */
53
+ forceNow?: boolean;
54
+ /** Limit this tick to one series id. */
55
+ onlyAccountId?: string;
56
+ /** A subject the person named. It becomes the one manual candidate of this run; feeds are not read. */
57
+ about?: string;
58
+ /** A script the coding agent wrote. It replaces the writer for the one-off job `tick` makes. */
59
+ script?: ScriptFile;
60
+ /** Why a series made nothing this tick. tick fills it in; the CLI prints it. */
61
+ notes?: Array<{ series: string; message: string }>;
62
+ /** Renders with the engine the stage chose. Test fakes may ignore the third argument. */
63
+ render: (content: ContentPackage, outputPath: string, engine: EngineId, scene?: SceneRenderContext) => Promise<void>;
64
+ /** A scene folder the agent wrote (`--scene DIR`). The render stage copies it into the run. */
65
+ scene?: string;
66
+ /** An engine the person named for this run (`--engine`). It beats the series and the machine default. */
67
+ engine?: string;
68
+ /** What each engine needs on this machine. Defaults to probing the real machine. */
69
+ probes?: () => Promise<EngineProbes> | EngineProbes;
70
+ /** The project whose `videos/` folder gets the finished file under a readable name. Null or absent: `renders/<jobId>.mp4`. */
71
+ project?: Project | null;
72
+ /** How long one claim holds a job. The worker renews it while the job runs. */
73
+ leaseMs?: number;
74
+ /** True once the worker was asked to stop. A job stops at the next safe point and its lease is released. */
75
+ shouldStop?: () => boolean;
76
+ /** What the after-render quality checks read the file with. Defaults to ffprobe and ffmpeg on this machine. */
77
+ review?: import("./review.js").ReviewTools;
78
+ /** No bytes for this long aborts a binary upload. Default 120 s. */
79
+ uploadIdleMs?: number;
80
+ }
81
+
82
+ const NO_KEYS_MESSAGE = "No platform key is set, so no comments were read and no replies were sent.";
83
+
84
+ export const LEASE_MS = 20 * 60_000;
85
+
86
+ /** The job's own view: desk settings, plus desk secrets in a local env. process.env is never touched. */
87
+ function deskView(runtime: Runtime, account: Account, withSecrets: boolean): { runtime: Runtime; account: Account } {
88
+ const overlay = runtime.store.deskOverlay(account.id);
89
+ const applied = overlay ? applyDesk(account, overlay, runtime.config) : { account, config: runtime.config };
90
+ if (!withSecrets) return { runtime: { ...runtime, config: applied.config }, account: applied.account };
91
+ const secretEnv = secretEnvFor(runtime.store, applied.account, runtime.env.CREDENTIALS_KEY);
92
+ return {
93
+ runtime: { ...runtime, config: configWithSecrets(applied.config, secretEnv), env: { ...runtime.env, ...secretEnv } },
94
+ account: applied.account,
95
+ };
96
+ }
97
+
98
+ export async function tick(runtime: Runtime): Promise<number> {
99
+ return (await tickJobs(runtime)).length;
100
+ }
101
+
102
+ /** The manual candidate for a subject the person named. Each ask is its own, so asking twice is not "seen". */
103
+ export function aboutCandidate(subject: string, now: Date): Candidate {
104
+ const url = `manual:about/${now.getTime()}`;
105
+ return { fingerprint: fingerprint("manual", url, subject), provider: "manual", title: subject, url, excerpt: subject, publishedAt: now.toISOString() };
106
+ }
107
+
108
+ /** Runs a tick and returns the ids of the jobs it processed, in order. */
109
+ export async function tickJobs(runtime: Runtime): Promise<string[]> {
110
+ const now = runtime.now();
111
+ seedDesk(runtime.store, runtime.config.accounts);
112
+ const accounts = usableSeries(
113
+ runtime.store,
114
+ deskAccounts(runtime.store).filter((account) => !runtime.onlyAccountId || account.id === runtime.onlyAccountId),
115
+ );
116
+ const oneOff = Boolean(runtime.about || runtime.script);
117
+ const own: string[] = [];
118
+ for (const account of accounts) {
119
+ if (oneOff && runtime.script && own.length > 0) break;
120
+ const view = deskView(runtime, account, false);
121
+ // A job that waits for review holds no worker and does not stop the cadence: the next due job is made, and waits too.
122
+ for (const id of runtime.store.awaitingReviewJobs(account.id)) {
123
+ runtime.notes?.push({ series: account.id, message: `job ${id} waits for review — npx vfrey desk job approve --id ${id}` });
124
+ }
125
+ const decision = dueDecision(view.runtime, view.account, now);
126
+ if (!decision.due) {
127
+ log("info", "account not due", { accountId: account.id, reason: decision.reason });
128
+ runtime.notes?.push({ series: account.id, message: decision.reason === "paused" ? "paused" : `not due: ${decision.reason}` });
129
+ continue;
130
+ }
131
+ if (oneOff) {
132
+ // A video the person asked for is its own job. A series job that is running or waiting does not block it.
133
+ const subject = (runtime.about ?? runtime.script?.subject ?? runtime.script?.title ?? "").trim();
134
+ const manual = aboutCandidate(subject, now);
135
+ const payload = { ...emptyPayload(), candidates: [manual], ...(runtime.script ? { script: runtime.script } : {}) };
136
+ own.push(runtime.store.enqueue({ accountId: account.id, kind: "pipeline", payload, standalone: true }).id);
137
+ continue;
138
+ }
139
+ const job = runtime.store.enqueue({ accountId: account.id, kind: "pipeline" });
140
+ if (job.status === "leased" && job.leaseUntil) {
141
+ runtime.notes?.push({
142
+ series: account.id,
143
+ message: `job ${job.id} in progress until ${job.leaseUntil} — npx vfrey desk job cancel --id ${job.id}`,
144
+ });
145
+ } else if (job.status === "pending" && new Date(job.runAfter).getTime() > now.getTime()) {
146
+ runtime.notes?.push({ series: account.id, message: `job ${job.id} waits to retry at ${job.runAfter} — npx vfrey desk job cancel --id ${job.id}` });
147
+ }
148
+ }
149
+ const processed: string[] = [];
150
+ if (oneOff) {
151
+ // Only the jobs this ask made run. Series jobs left over from earlier ticks wait for a plain tick.
152
+ for (const id of own) {
153
+ if (runtime.shouldStop?.()) break;
154
+ const job = runtime.store.claim(runtime.now(), runtime.leaseMs ?? LEASE_MS, id);
155
+ if (!job) continue;
156
+ await processJob(runtime, job);
157
+ processed.push(job.id);
158
+ }
159
+ return processed;
160
+ }
161
+ for (;;) {
162
+ if (runtime.shouldStop?.()) break;
163
+ const job = runtime.store.claim(runtime.now(), runtime.leaseMs ?? LEASE_MS);
164
+ if (!job) break;
165
+ await processJob(runtime, job);
166
+ processed.push(job.id);
167
+ if (processed.length >= 3) break;
168
+ }
169
+ return processed;
170
+ }
171
+
172
+ export function dueDecision(runtime: Pick<Runtime, "store" | "forceNow">, account: Account, now: Date) {
173
+ if (runtime.store.seriesPaused(account.id)) return { due: false, reason: "paused" };
174
+ // The person asked for a video now: no window, gap, or daily count. A paused series was refused above.
175
+ if (runtime.forceNow) return { due: true, reason: "asked" };
176
+ // The cadence counts videos made, so a series that cannot publish does not render forever.
177
+ const times = runtime.store.videoTimes(account.id);
178
+ const day = localDayKey(now, account.cadence.timezone);
179
+ const madeOnLocalDay = times.filter((time) => localDayKey(time, account.cadence.timezone) === day).length;
180
+ return cadenceDecision({
181
+ cadence: account.cadence,
182
+ now,
183
+ lastPublishedAt: times[0] ?? null,
184
+ postsOnLocalDay: madeOnLocalDay,
185
+ });
186
+ }
187
+
188
+ export async function processJob(parent: Runtime, job: JobRecord): Promise<void> {
189
+ if (deskIsEmpty(parent.store)) seedDesk(parent.store, parent.config.accounts);
190
+ const found = deskAccounts(parent.store).find((item) => item.id === job.accountId);
191
+ if (!found) {
192
+ const message = `Series ${job.accountId} is not in the desk.`;
193
+ parent.store.recordEvent(job.id, { stage: job.stage, status: "blocked", code: "unknown_series", message, durationMs: null, attempt: job.attempt });
194
+ parent.store.finish(job, "blocked", `unknown_series: ${message}`);
195
+ return;
196
+ }
197
+ const view = deskView(parent, found, true);
198
+ const runtime = view.runtime;
199
+ const account = view.account;
200
+ const leaseMs = runtime.leaseMs ?? LEASE_MS;
201
+ const renew = setInterval(() => {
202
+ try {
203
+ if (!runtime.store.renewLease(job, leaseMs, runtime.now())) log("warn", "lease lost", { jobId: job.id });
204
+ } catch (error) {
205
+ log("warn", "lease renewal failed", { jobId: job.id, message: error instanceof Error ? error.message : String(error) });
206
+ }
207
+ }, Math.max(10, Math.floor(leaseMs / 3)));
208
+ renew.unref();
209
+ try {
210
+ await runJob(runtime, account, job);
211
+ } catch (error) {
212
+ // The claim was cancelled or taken over. The new owner of the job owns its stored sessions; this worker writes nothing more.
213
+ if (!(error instanceof LeaseLost)) throw error;
214
+ log("warn", "claim lost", { jobId: job.id });
215
+ runtime.store.releaseLeaseOwner(job);
216
+ } finally {
217
+ clearInterval(renew);
218
+ }
219
+ }
220
+
221
+ async function runJob(runtime: Runtime, account: Account, job: JobRecord): Promise<void> {
222
+ try {
223
+ if (job.kind === "analytics") {
224
+ await runAnalytics(runtime, account);
225
+ runtime.store.finish(job, "succeeded", null);
226
+ return;
227
+ }
228
+ if (job.kind === "engage") {
229
+ if (noPlatformKeys(runtime, account)) {
230
+ runtime.store.recordTerminal(job.id, { stage: "engage", status: "skipped", code: "no_keys", message: NO_KEYS_MESSAGE, attempt: job.attempt });
231
+ } else {
232
+ await runEngage(runtime, account, null);
233
+ }
234
+ runtime.store.finish(job, "succeeded", null);
235
+ return;
236
+ }
237
+ await runPipeline(runtime, account, job);
238
+ } catch (error) {
239
+ if (error instanceof LeaseLost) throw error;
240
+ if (error instanceof StopRequested) {
241
+ runtime.store.releaseWithoutAttempt(job, runtime.now(), null);
242
+ runtime.store.recordTerminal(job.id, {
243
+ stage: job.stage,
244
+ status: "retrying",
245
+ code: "shutdown",
246
+ message: "The worker stopped. The job resumes from this stage on the next tick.",
247
+ attempt: job.attempt,
248
+ });
249
+ return;
250
+ }
251
+ const appError = asAppError(error);
252
+ log(appError.retryable ? "warn" : "error", "job stopped", {
253
+ jobId: job.id,
254
+ accountId: account.id,
255
+ stage: job.stage,
256
+ code: appError.code,
257
+ risk: appError.risk,
258
+ message: appError.message,
259
+ });
260
+ if (appError instanceof QualityError) {
261
+ runtime.store.recordTerminal(job.id, {
262
+ stage: job.stage,
263
+ status: "rejected",
264
+ code: appError.code,
265
+ message: appError.message,
266
+ attempt: job.attempt,
267
+ });
268
+ runtime.store.finish(job, "rejected", `${appError.code}: ${appError.message}`);
269
+ return;
270
+ }
271
+ if (appError instanceof ComplianceError || (appError instanceof AppError && appError.risk === "config")) {
272
+ runtime.store.recordTerminal(job.id, {
273
+ stage: job.stage,
274
+ status: "blocked",
275
+ code: appError.code,
276
+ message: appError.message,
277
+ attempt: job.attempt,
278
+ });
279
+ runtime.store.finish(job, "blocked", `${appError.code}: ${appError.message}`);
280
+ return;
281
+ }
282
+ const wait = backoffMs({
283
+ attempt: job.attempt,
284
+ retryAfterMs: appError instanceof RetryableError ? appError.retryAfterMs : undefined,
285
+ random: () => 0.5,
286
+ });
287
+ const retryAt = new Date(runtime.now().getTime() + wait);
288
+ runtime.store.releaseRetry(job, retryAt, `${appError.code}: ${appError.message}`);
289
+ const exhausted = job.status === "dead";
290
+ runtime.store.recordTerminal(job.id, {
291
+ stage: job.stage,
292
+ status: exhausted ? "dead" : "retrying",
293
+ code: appError.code,
294
+ message: exhausted
295
+ ? `${appError.message} (attempts exhausted)`
296
+ : `${appError.message} (next attempt after ${retryAt.toISOString()})`,
297
+ attempt: job.attempt,
298
+ });
299
+ }
300
+ }
301
+
302
+ async function runPipeline(runtime: Runtime, account: Account, job: JobRecord): Promise<void> {
303
+ const data = job.payload;
304
+ const { store } = runtime;
305
+ const begin = (stage: string) =>
306
+ store.recordEvent(job.id, { stage, status: "running", code: null, message: null, durationMs: null, attempt: job.attempt });
307
+ const end = (stage: string, status: string, code: string | null = null, message: string | null = null) =>
308
+ store.recordTerminal(job.id, { stage, status, code, message, attempt: job.attempt });
309
+ const safePoint = () => {
310
+ if (runtime.shouldStop?.()) throw new StopRequested();
311
+ };
312
+ if (job.stage === "source") {
313
+ safePoint();
314
+ begin("source");
315
+ await stageSource(runtime, account, data, job);
316
+ end("source", "succeeded");
317
+ job.stage = "generate";
318
+ store.save(job);
319
+ }
320
+ if (job.stage === "generate") {
321
+ safePoint();
322
+ begin("generate");
323
+ await stageGenerate(runtime, account, data);
324
+ end("generate", "succeeded");
325
+ job.stage = "render";
326
+ store.save(job);
327
+ }
328
+ if (job.stage === "render") {
329
+ safePoint();
330
+ begin("render");
331
+ if ((await stageRender(runtime, account, job, data)) === "awaiting_review") {
332
+ // The series asked to see this before it renders. Hand back the claim: nothing holds a worker while the person looks.
333
+ (data as { review?: { state: string; at?: string } }).review = { state: "waiting", at: runtime.now().toISOString() };
334
+ store.holdForReview(job);
335
+ end("render", "awaiting_review", "awaiting_review", `Waiting for review. Approve: npx vfrey desk job approve --id ${job.id}`);
336
+ return;
337
+ }
338
+ const used = (data as { engine?: { label?: string } }).engine;
339
+ end("render", "succeeded", null, used?.label ? `Engine: ${used.label}` : null);
340
+ job.stage = "publish";
341
+ store.save(job);
342
+ }
343
+ if (job.stage === "publish") {
344
+ safePoint();
345
+ begin("publish");
346
+ const deferral = await stagePublish(runtime, account, job, data);
347
+ if (deferral) {
348
+ const note = `${deferral.code}: ${deferral.message}`;
349
+ if (deferral.countsAttempt) {
350
+ store.releaseRetry(job, deferral.until, note);
351
+ } else {
352
+ store.releaseWithoutAttempt(job, deferral.until, note);
353
+ }
354
+ const dead = job.status === "dead";
355
+ end(
356
+ "publish",
357
+ dead ? "dead" : "retrying",
358
+ deferral.code,
359
+ dead ? `${deferral.message} (attempts exhausted)` : `${deferral.message} (next try after ${deferral.until.toISOString()})`,
360
+ );
361
+ return;
362
+ }
363
+ const sessions = Object.entries(data.platforms);
364
+ const skipCodes = sessions.flatMap(([, item]) => (item?.phase === "skipped" && item.errorCode ? [item.errorCode] : []));
365
+ const skipCode = skipCodes.includes("publish_off") ? "publish_off" : skipCodes.includes("daily_cap") ? "daily_cap" : skipCodes.includes("missing_key") ? "missing_key" : null;
366
+ const onlySkips = sessions.every(([, item]) => !item || item.phase === "skipped");
367
+ const live = store.seriesGates(account.id).live || runtime.dryRun;
368
+ const summary = (live ? "" : "dry-run, the series is not live: ") + sessions.map(([platform, item]) => `${platform}: ${item?.errorCode ?? item?.phase ?? "none"}`).join("; ");
369
+ end("publish", skipCode && onlySkips ? "skipped" : "succeeded", skipCode && onlySkips ? skipCode : null, summary || null);
370
+ job.stage = "engage";
371
+ store.save(job);
372
+ }
373
+ if (job.stage === "engage") {
374
+ safePoint();
375
+ begin("engage");
376
+ let skippedWith: string | null = null;
377
+ try {
378
+ if (noPlatformKeys(runtime, account)) skippedWith = "no_keys";
379
+ else data.engageReplied = await runEngage(runtime, account, data);
380
+ } catch (error) {
381
+ // The videos are published. Replies are never worth ending a published job dead.
382
+ const appError = asAppError(error);
383
+ log("warn", "engage skipped after publish", { jobId: job.id, code: appError.code, risk: appError.risk });
384
+ skippedWith = appError.code;
385
+ }
386
+ job.payload = data;
387
+ end("engage", skippedWith ? "skipped" : "succeeded", skippedWith, skippedWith === "no_keys" ? NO_KEYS_MESSAGE : null);
388
+ store.finish(job, "succeeded", null, runtime.now());
389
+ store.recordEvent(job.id, { stage: "done", status: "succeeded", code: null, message: null, durationMs: 0, attempt: job.attempt });
390
+ }
391
+ }
392
+
393
+ export async function defaultRender(
394
+ content: ContentPackage,
395
+ outputPath: string,
396
+ dataDir: string,
397
+ engine: EngineId = "remotion",
398
+ env: Env = process.env,
399
+ scene?: SceneRenderContext,
400
+ ): Promise<void> {
401
+ const forced = forcedFailure(engine, env);
402
+ if (forced) throw new Error(forced);
403
+ if (scene) {
404
+ if (engine !== "remotion" && engine !== "vfrey-framey" && engine !== "hyperframes") throw new Error(`${engine} does not render scenes.`);
405
+ const { renderScene } = await import("../scene/render.js");
406
+ await renderScene({
407
+ engine,
408
+ sceneDir: scene.dir,
409
+ title: content.title,
410
+ lines: content.voiceoverScript.split("\n").map((line) => line.trim()).filter(Boolean),
411
+ beats: content.beats.map((beat) => ({ role: beat.role, text: beat.text, caption: beat.caption })),
412
+ voiceoverPath: content.voiceoverPath,
413
+ outputPath,
414
+ workDir: path.join(dataDir, "public", path.basename(outputPath, ".mp4"), `scene-${engine}`),
415
+ env,
416
+ warn: scene.warn,
417
+ });
418
+ return;
419
+ }
420
+ const publicDir = path.join(dataDir, "public", path.basename(outputPath, ".mp4"));
421
+ if (engine === "vfrey-framey" || engine === "hyperframes") {
422
+ await renderHtmlEngine({
423
+ engine,
424
+ props: toRenderProps(content, null),
425
+ voiceoverPath: content.voiceoverPath,
426
+ outputPath,
427
+ workDir: path.join(publicDir, engine),
428
+ env,
429
+ });
430
+ return;
431
+ }
432
+ if (engine !== "remotion") throw new Error(`${engine} has no renderer yet.`);
433
+ const { renderMp4 } = await import("../render/worker.js");
434
+ // The picture follows the voice, as it does for the HTML engines, so a short voice is not padded with silence.
435
+ const props = retimeToVoice(toRenderProps(content, content.voiceoverPath ? "voice.mp3" : null), content.voiceoverPath ? await voiceSeconds(content.voiceoverPath, env) : null);
436
+ await renderMp4({
437
+ props,
438
+ voiceoverPath: content.voiceoverPath,
439
+ outputPath,
440
+ publicDir,
441
+ entryPoint: path.join(path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../.."), "src/remotion/index.ts"),
442
+ });
443
+ }
@@ -0,0 +1,26 @@
1
+ import type { Account } from "@vfrey/contracts";
2
+ import { log } from "../log.js";
3
+ import { validateCadence } from "../config.js";
4
+ import type { Store } from "../queue/store.js";
5
+
6
+ /**
7
+ * Keeps the series whose cadence can run. A series with a bad timezone or window is left out of
8
+ * this tick with one blocked row that says why, and the others go on.
9
+ */
10
+ export function usableSeries(store: Store, accounts: Account[]): Account[] {
11
+ return accounts.filter((account) => {
12
+ const fault = validateCadence(account.cadence);
13
+ if (!fault) return true;
14
+ const error = `invalid_cadence: ${fault}`;
15
+ log("warn", "series skipped", { accountId: account.id, reason: error });
16
+ const seen = store.db
17
+ .prepare(`SELECT 1 FROM jobs WHERE account_id = ? AND status = 'blocked' AND last_error = ? LIMIT 1`)
18
+ .get(account.id, error);
19
+ if (!seen) {
20
+ const job = store.enqueue({ accountId: account.id, kind: "pipeline" });
21
+ store.recordEvent(job.id, { stage: job.stage, status: "blocked", code: "invalid_cadence", message: fault, durationMs: null, attempt: 0 });
22
+ store.finish(job, "blocked", error);
23
+ }
24
+ return false;
25
+ });
26
+ }
@@ -0,0 +1,69 @@
1
+ // Fetches a series' feed, subreddit and YouTube sources one at a time, so each source gets its own result:
2
+ // n candidates, or the error code. The result is stored on the source row for the Sources screen.
3
+ import { collectCandidates } from "@vfrey/ingest";
4
+ import { log } from "@vfrey/contracts";
5
+ import type { Account, Candidate } from "@vfrey/contracts";
6
+ import type { Runtime } from "./run.js";
7
+
8
+ const isHttp = (url: string): boolean => {
9
+ try {
10
+ const protocol = new URL(url).protocol;
11
+ return protocol === "http:" || protocol === "https:";
12
+ } catch {
13
+ return false;
14
+ }
15
+ };
16
+
17
+ type Sources = Account["sources"];
18
+ const alone = (account: Account, sources: Partial<Sources>): Account => ({
19
+ ...account,
20
+ sources: { rss: [], subreddits: [], youtubeChannelIds: [], youtubeTrendingRegion: null, ...sources },
21
+ });
22
+
23
+ /** The error code of a thrown source failure. */
24
+ export const codeOf = (error: unknown): string => {
25
+ const code = (error as { code?: unknown } | null)?.code;
26
+ return typeof code === "string" && code ? code : "source_failed";
27
+ };
28
+
29
+ /**
30
+ * Collects every non-Topics source of the series on its own and records each result. Behaves like one
31
+ * `collectCandidates` call: a failed source never discards the others, and when nothing was collected and a
32
+ * source failed, the first failure is rethrown so the worker can retry or stop.
33
+ */
34
+ export async function collectPerSource(runtime: Pick<Runtime, "config" | "fetchImpl" | "now" | "store">, account: Account): Promise<Candidate[]> {
35
+ // What to read comes from the account (the desk rows, overlaid on the base account); the row id is only for the record.
36
+ const rows = runtime.store.db.prepare(`SELECT id, kind, value FROM desk_sources WHERE series_id = ?`).all(account.id) as Array<{ id: string; kind: string; value: string }>;
37
+ const rowId = (kind: string, value: string) => rows.find((row) => row.kind === kind && row.value === value)?.id ?? null;
38
+ const plan: Array<{ kind: string; value: string; sources: Partial<Sources> }> = [
39
+ ...account.sources.rss.map((value) => ({ kind: "rss", value, sources: { rss: [value] } })),
40
+ ...account.sources.subreddits.map((value) => ({ kind: "subreddit", value, sources: { subreddits: [value] } })),
41
+ ...account.sources.youtubeChannelIds.map((value) => ({ kind: "youtubeChannel", value, sources: { youtubeChannelIds: [value] } })),
42
+ ...(account.sources.youtubeTrendingRegion ? [{ kind: "youtubeTrending", value: account.sources.youtubeTrendingRegion, sources: { youtubeTrendingRegion: account.sources.youtubeTrendingRegion } }] : []),
43
+ ];
44
+ const found: Candidate[] = [];
45
+ const thrown: unknown[] = [];
46
+ const record = (kind: string, value: string, count: number | null, code: string | null) => {
47
+ const id = rowId(kind, value);
48
+ if (id) runtime.store.recordSourceFetch(id, { at: runtime.now(), count, code, status: code ? "error" : "ok" });
49
+ };
50
+ for (const item of plan) {
51
+ if (item.kind === "rss" && !isHttp(item.value)) {
52
+ log("warn", "source url rejected", { accountId: account.id, code: "source_url_rejected", url: item.value });
53
+ record(item.kind, item.value, null, "source_url_rejected");
54
+ continue;
55
+ }
56
+ try {
57
+ const items = await collectCandidates({ config: runtime.config, account: alone(account, item.sources), fetchImpl: runtime.fetchImpl, manual: [] });
58
+ found.push(...items);
59
+ record(item.kind, item.value, items.length, null);
60
+ } catch (error) {
61
+ thrown.push(error);
62
+ record(item.kind, item.value, null, codeOf(error));
63
+ }
64
+ }
65
+ if (found.length === 0 && thrown.length > 0) throw thrown[0];
66
+ const deduped = new Map<string, Candidate>();
67
+ for (const candidate of found) if (!deduped.has(candidate.fingerprint)) deduped.set(candidate.fingerprint, candidate);
68
+ return [...deduped.values()].slice(0, 30);
69
+ }