@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.
Files changed (603) 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 +282 -0
  40. package/src/api/commands.ts +330 -0
  41. package/src/api/config.ts +34 -0
  42. package/src/api/creators.ts +463 -0
  43. package/src/api/credentials.ts +152 -0
  44. package/src/api/mail.ts +333 -0
  45. package/src/api/topics.ts +207 -0
  46. package/src/api/types.ts +205 -0
  47. package/src/cli.ts +467 -0
  48. package/src/cloud/executor.ts +281 -0
  49. package/src/cloud/job.ts +106 -0
  50. package/src/cloud/render-job.ts +187 -0
  51. package/src/config.ts +309 -0
  52. package/src/dashboard-link.ts +180 -0
  53. package/src/dashboard-start.ts +103 -0
  54. package/src/desk/channel.ts +64 -0
  55. package/src/desk/cli.ts +216 -0
  56. package/src/desk/creators-route.ts +129 -0
  57. package/src/desk/fonts/JetBrainsMono-OFL.txt +93 -0
  58. package/src/desk/fonts/google-sans-flex-latin.woff2 +0 -0
  59. package/src/desk/fonts/jetbrains-mono-medium.woff2 +0 -0
  60. package/src/desk/http.ts +460 -0
  61. package/src/desk/mail-route.ts +103 -0
  62. package/src/desk/overlay.ts +112 -0
  63. package/src/desk/preview.ts +422 -0
  64. package/src/desk/screens.ts +149 -0
  65. package/src/desk/secrets.ts +143 -0
  66. package/src/desk/series.ts +127 -0
  67. package/src/desk/state.ts +77 -0
  68. package/src/desk/studio-route.ts +119 -0
  69. package/src/desk/tick-runner.ts +66 -0
  70. package/src/desk/tick.ts +56 -0
  71. package/src/desk/topics-route.ts +41 -0
  72. package/src/desk/ui/app.css +505 -0
  73. package/src/desk/ui/app.js +185 -0
  74. package/src/desk/ui/apple-icon.png +0 -0
  75. package/src/desk/ui/components.js +79 -0
  76. package/src/desk/ui/core.js +156 -0
  77. package/src/desk/ui/favicon.ico +0 -0
  78. package/src/desk/ui/icon.svg +1 -0
  79. package/src/desk/ui/icons.js +49 -0
  80. package/src/desk/ui/index.html +41 -0
  81. package/src/desk/ui/metrics-math.d.ts +22 -0
  82. package/src/desk/ui/metrics-math.js +135 -0
  83. package/src/desk/ui/theme.js +5 -0
  84. package/src/desk/ui/views-creators.js +209 -0
  85. package/src/desk/ui/views-keys.js +114 -0
  86. package/src/desk/ui/views-mail.js +188 -0
  87. package/src/desk/ui/views-misc.js +123 -0
  88. package/src/desk/ui/views-review.js +119 -0
  89. package/src/desk/ui/views-runs.js +350 -0
  90. package/src/desk/ui/views-screens.js +163 -0
  91. package/src/desk/ui/views-series.js +318 -0
  92. package/src/desk/ui/views-topics.js +87 -0
  93. package/src/desk/verbs.ts +361 -0
  94. package/src/doctor.ts +212 -0
  95. package/src/drain.ts +14 -0
  96. package/src/engage/index.ts +217 -0
  97. package/src/generate/index.ts +110 -0
  98. package/src/generate/script.ts +152 -0
  99. package/src/generate/title.ts +78 -0
  100. package/src/help.ts +233 -0
  101. package/src/home.ts +45 -0
  102. package/src/llm/client.ts +177 -0
  103. package/src/log.ts +7 -0
  104. package/src/oauth/tokens.ts +189 -0
  105. package/src/oauth/vault.ts +72 -0
  106. package/src/pipeline/review.ts +642 -0
  107. package/src/pipeline/run.ts +443 -0
  108. package/src/pipeline/series-guard.ts +26 -0
  109. package/src/pipeline/source-fetch.ts +69 -0
  110. package/src/pipeline/stages.ts +728 -0
  111. package/src/pipeline/topics-source.ts +90 -0
  112. package/src/product/commands.ts +296 -0
  113. package/src/product/config-file.ts +38 -0
  114. package/src/product/engine-choice.ts +89 -0
  115. package/src/product/engines.ts +103 -0
  116. package/src/product/insights.ts +50 -0
  117. package/src/product/keys.ts +67 -0
  118. package/src/product/paths.ts +18 -0
  119. package/src/product/probes.ts +88 -0
  120. package/src/product/push-flow.ts +90 -0
  121. package/src/product/render-video.ts +21 -0
  122. package/src/product/short.ts +62 -0
  123. package/src/product/update.ts +32 -0
  124. package/src/project/canonical.ts +14 -0
  125. package/src/project/commands.ts +177 -0
  126. package/src/project/dashboard-status.ts +28 -0
  127. package/src/project/discover.ts +19 -0
  128. package/src/project/index-file.ts +73 -0
  129. package/src/project/marker.ts +87 -0
  130. package/src/project/migrate.ts +201 -0
  131. package/src/project/names.ts +27 -0
  132. package/src/project/notice.ts +16 -0
  133. package/src/project/open.ts +88 -0
  134. package/src/project/scaffold.ts +112 -0
  135. package/src/project/templates.ts +42 -0
  136. package/src/project/videos.ts +64 -0
  137. package/src/publish/facebook.ts +91 -0
  138. package/src/publish/index.ts +12 -0
  139. package/src/publish/instagram.ts +97 -0
  140. package/src/publish/rate.ts +62 -0
  141. package/src/publish/session.ts +242 -0
  142. package/src/publish/tiktok.ts +202 -0
  143. package/src/publish/youtube.ts +177 -0
  144. package/src/queue/desk-ops.ts +1170 -0
  145. package/src/queue/seal.ts +3 -0
  146. package/src/queue/store.ts +1006 -0
  147. package/src/remotion/AiLabel.tsx +26 -0
  148. package/src/remotion/Root.tsx +73 -0
  149. package/src/remotion/defaults.ts +26 -0
  150. package/src/remotion/films/claude/ClaudeFilm.tsx +1014 -0
  151. package/src/remotion/films/engine/EngineMark.tsx +17 -0
  152. package/src/remotion/films/rea/ReaShort.tsx +552 -0
  153. package/src/remotion/films/rea/drawings.tsx +907 -0
  154. package/src/remotion/films/rea/timing.ts +144 -0
  155. package/src/remotion/index.ts +4 -0
  156. package/src/remotion/templates/EditorialSplit.tsx +39 -0
  157. package/src/remotion/templates/KineticType.tsx +39 -0
  158. package/src/remotion/templates/SignalBoard.tsx +50 -0
  159. package/src/remotion/timing.ts +22 -0
  160. package/src/render/chrome.ts +78 -0
  161. package/src/render/engine-mark.ts +29 -0
  162. package/src/render/framey-template.ts +130 -0
  163. package/src/render/html-engine.ts +149 -0
  164. package/src/render/node-crypto-stub.ts +11 -0
  165. package/src/render/probe.ts +29 -0
  166. package/src/render/props.ts +17 -0
  167. package/src/render/vertical-short.ts +35 -0
  168. package/src/render/webpack.ts +33 -0
  169. package/src/render/worker.ts +65 -0
  170. package/src/scene/assets/fonts/TitanOne-OFL.txt +94 -0
  171. package/src/scene/assets/fonts/titan-one.woff2 +0 -0
  172. package/src/scene/assets/gsap/LICENSE.txt +8 -0
  173. package/src/scene/assets/gsap/gsap.min.js +11 -0
  174. package/src/scene/audio.ts +54 -0
  175. package/src/scene/blocks/LICENSE +190 -0
  176. package/src/scene/blocks/NOTICE +5 -0
  177. package/src/scene/blocks/bar-chart-race/bar-chart-race.html +585 -0
  178. package/src/scene/blocks/beat-freeze-cut/beat-freeze-cut.html +874 -0
  179. package/src/scene/blocks/camcorder-hud/camcorder-hud.html +164 -0
  180. package/src/scene/blocks/camera-dolly-zoom/camera-dolly-zoom.html +355 -0
  181. package/src/scene/blocks/code-diff/code-diff.html +817 -0
  182. package/src/scene/blocks/code-snippet-apple-terminal-pro/code-snippet-apple-terminal-pro.html +248 -0
  183. package/src/scene/blocks/code-typing/code-typing.html +764 -0
  184. package/src/scene/blocks/data-chart/data-chart.html +452 -0
  185. package/src/scene/blocks/flowchart-vertical/flowchart-vertical.html +454 -0
  186. package/src/scene/blocks/hw-scribble-transition/hw-scribble-transition.html +153 -0
  187. package/src/scene/blocks/index.json +824 -0
  188. package/src/scene/blocks/lt-accent-underline/lt-accent-underline.html +107 -0
  189. package/src/scene/blocks/lt-bold-block/lt-bold-block.html +109 -0
  190. package/src/scene/blocks/lt-clean-bar/lt-clean-bar.html +112 -0
  191. package/src/scene/blocks/lt-dark-card/lt-dark-card.html +111 -0
  192. package/src/scene/blocks/lt-kicker-name/lt-kicker-name.html +114 -0
  193. package/src/scene/blocks/lt-mask-reveal/lt-mask-reveal.html +120 -0
  194. package/src/scene/blocks/lt-neon-border/lt-neon-border.html +391 -0
  195. package/src/scene/blocks/lt-stack-bars/lt-stack-bars.html +109 -0
  196. package/src/scene/blocks/macos-notification/macos-notification.html +194 -0
  197. package/src/scene/blocks/mk-background/mk-background.html +236 -0
  198. package/src/scene/blocks/mk-callout-highlight/mk-callout-highlight.html +183 -0
  199. package/src/scene/blocks/mk-clone-wall-transition/mk-clone-wall-transition.html +226 -0
  200. package/src/scene/blocks/mk-line-graph/mk-line-graph.html +293 -0
  201. package/src/scene/blocks/mk-progress-stat/mk-progress-stat.html +186 -0
  202. package/src/scene/blocks/mk-specs-list/mk-specs-list.html +223 -0
  203. package/src/scene/blocks/news-ticker/news-ticker.html +409 -0
  204. package/src/scene/blocks/organic-light-leak-overlay/organic-light-leak-overlay.html +126 -0
  205. package/src/scene/blocks/oscilloscope-trace/oscilloscope-trace.html +519 -0
  206. package/src/scene/blocks/snow/snow.html +205 -0
  207. package/src/scene/blocks/split-flap-board/split-flap-board.html +347 -0
  208. package/src/scene/blocks/spotify-card/spotify-card.html +294 -0
  209. package/src/scene/blocks/starfield/starfield.html +188 -0
  210. package/src/scene/blocks/thread-message-stack/thread-message-stack.html +239 -0
  211. package/src/scene/blocks/week-in-merges/week-in-merges.html +470 -0
  212. package/src/scene/blocks/x-post/x-post.html +437 -0
  213. package/src/scene/blocks/yt-comment-card/yt-comment-card.html +319 -0
  214. package/src/scene/blocks/yt-lcd-background/yt-lcd-background.html +307 -0
  215. package/src/scene/blocks/yt-prism-title/yt-prism-title.html +205 -0
  216. package/src/scene/blocks.ts +229 -0
  217. package/src/scene/command.ts +272 -0
  218. package/src/scene/figure.ts +181 -0
  219. package/src/scene/glyphs.ts +107 -0
  220. package/src/scene/init.ts +86 -0
  221. package/src/scene/inspect/css-tree.d.ts +6 -0
  222. package/src/scene/inspect/page-scripts.ts +272 -0
  223. package/src/scene/inspect/report.ts +405 -0
  224. package/src/scene/inspect/run.ts +200 -0
  225. package/src/scene/inspect/source.ts +236 -0
  226. package/src/scene/label.ts +131 -0
  227. package/src/scene/net-guard.ts +74 -0
  228. package/src/scene/render.ts +292 -0
  229. package/src/scene/runtime/browser.ts +199 -0
  230. package/src/scene/runtime/core.ts +181 -0
  231. package/src/scene/runtime/react.tsx +115 -0
  232. package/src/scene/runtime/transitions.ts +133 -0
  233. package/src/scene/sanitize.ts +181 -0
  234. package/src/scene/scene.ts +152 -0
  235. package/src/scene/studio-command.ts +48 -0
  236. package/src/scene/studio-skin.ts +59 -0
  237. package/src/scene/studio.ts +337 -0
  238. package/src/scene/supply.ts +111 -0
  239. package/src/scene/timing.ts +288 -0
  240. package/src/scene/transitions/LICENSE +190 -0
  241. package/src/scene/transitions/NOTICE +5 -0
  242. package/src/scene/transitions/SOURCE.json +24 -0
  243. package/src/scene/transitions/transitions.global.js +38 -0
  244. package/src/scene/transitions.ts +26 -0
  245. package/src/service/all-projects.ts +153 -0
  246. package/src/service/commands.ts +256 -0
  247. package/src/service/due.ts +82 -0
  248. package/src/service/lock.ts +101 -0
  249. package/src/service/log-file.ts +56 -0
  250. package/src/service/state.ts +41 -0
  251. package/src/service/units.ts +90 -0
  252. package/src/service/worker.ts +51 -0
  253. package/src/skill-agents.ts +74 -0
  254. package/src/skill-install.ts +334 -0
  255. package/src/sync-write.ts +21 -0
  256. package/src/tick-report.ts +205 -0
  257. package/src/tts.ts +124 -0
  258. package/vendor/@vfrey/contracts/dist/account.d.ts +97 -0
  259. package/vendor/@vfrey/contracts/dist/account.js +27 -0
  260. package/vendor/@vfrey/contracts/dist/checks.d.ts +8 -0
  261. package/vendor/@vfrey/contracts/dist/checks.js +1 -0
  262. package/vendor/@vfrey/contracts/dist/credentials/crypto.d.ts +4 -0
  263. package/vendor/@vfrey/contracts/dist/credentials/crypto.js +38 -0
  264. package/vendor/@vfrey/contracts/dist/domain/advice-persona.d.ts +10 -0
  265. package/vendor/@vfrey/contracts/dist/domain/advice-persona.js +24 -0
  266. package/vendor/@vfrey/contracts/dist/domain/backoff.d.ts +13 -0
  267. package/vendor/@vfrey/contracts/dist/domain/backoff.js +27 -0
  268. package/vendor/@vfrey/contracts/dist/domain/cadence.d.ts +29 -0
  269. package/vendor/@vfrey/contracts/dist/domain/cadence.js +95 -0
  270. package/vendor/@vfrey/contracts/dist/domain/creators.d.ts +146 -0
  271. package/vendor/@vfrey/contracts/dist/domain/creators.js +2 -0
  272. package/vendor/@vfrey/contracts/dist/domain/disclosure.d.ts +127 -0
  273. package/vendor/@vfrey/contracts/dist/domain/disclosure.js +250 -0
  274. package/vendor/@vfrey/contracts/dist/domain/errors.d.ts +34 -0
  275. package/vendor/@vfrey/contracts/dist/domain/errors.js +68 -0
  276. package/vendor/@vfrey/contracts/dist/domain/redact.d.ts +1 -0
  277. package/vendor/@vfrey/contracts/dist/domain/redact.js +21 -0
  278. package/vendor/@vfrey/contracts/dist/domain/revenue.d.ts +13 -0
  279. package/vendor/@vfrey/contracts/dist/domain/revenue.js +16 -0
  280. package/vendor/@vfrey/contracts/dist/domain/safety.d.ts +20 -0
  281. package/vendor/@vfrey/contracts/dist/domain/safety.js +61 -0
  282. package/vendor/@vfrey/contracts/dist/domain/similarity.d.ts +13 -0
  283. package/vendor/@vfrey/contracts/dist/domain/similarity.js +100 -0
  284. package/vendor/@vfrey/contracts/dist/domain/templates.d.ts +18 -0
  285. package/vendor/@vfrey/contracts/dist/domain/templates.js +67 -0
  286. package/vendor/@vfrey/contracts/dist/domain/tiktok-chunks.d.ts +19 -0
  287. package/vendor/@vfrey/contracts/dist/domain/tiktok-chunks.js +52 -0
  288. package/vendor/@vfrey/contracts/dist/domain/topics.d.ts +78 -0
  289. package/vendor/@vfrey/contracts/dist/domain/topics.js +4 -0
  290. package/vendor/@vfrey/contracts/dist/domain/types.d.ts +216 -0
  291. package/vendor/@vfrey/contracts/dist/domain/types.js +1 -0
  292. package/vendor/@vfrey/contracts/dist/domain/variation.d.ts +57 -0
  293. package/vendor/@vfrey/contracts/dist/domain/variation.js +278 -0
  294. package/vendor/@vfrey/contracts/dist/events.d.ts +20 -0
  295. package/vendor/@vfrey/contracts/dist/events.js +1 -0
  296. package/vendor/@vfrey/contracts/dist/http.d.ts +11 -0
  297. package/vendor/@vfrey/contracts/dist/http.js +37 -0
  298. package/vendor/@vfrey/contracts/dist/index.d.ts +22 -0
  299. package/vendor/@vfrey/contracts/dist/index.js +22 -0
  300. package/vendor/@vfrey/contracts/dist/log.d.ts +2 -0
  301. package/vendor/@vfrey/contracts/dist/log.js +10 -0
  302. package/vendor/@vfrey/contracts/dist/mail.d.ts +55 -0
  303. package/vendor/@vfrey/contracts/dist/mail.js +1 -0
  304. package/vendor/@vfrey/contracts/package.json +18 -0
  305. package/vendor/@vfrey/ingest/dist/index.d.ts +41 -0
  306. package/vendor/@vfrey/ingest/dist/index.js +221 -0
  307. package/vendor/@vfrey/ingest/dist/parse.d.ts +5 -0
  308. package/vendor/@vfrey/ingest/dist/parse.js +165 -0
  309. package/vendor/@vfrey/ingest/package.json +21 -0
  310. package/vendor/framey/LICENSE +190 -0
  311. package/vendor/framey/NOTICE +15 -0
  312. package/vendor/framey/SOURCE.json +24 -0
  313. package/vendor/framey/STUDIO-SOURCE.json +9 -0
  314. package/vendor/framey/bin/framey-localize-fonts.mjs +12 -0
  315. package/vendor/framey/bin/framey.mjs +16 -0
  316. package/vendor/framey/dist/add-VMJD5KBS.js +75 -0
  317. package/vendor/framey/dist/animatedGifPrep-QPKGJ6PM.js +43 -0
  318. package/vendor/framey/dist/api.d.ts +211 -0
  319. package/vendor/framey/dist/api.js +76 -0
  320. package/vendor/framey/dist/appHistory-WW56FZTT.js +31 -0
  321. package/vendor/framey/dist/asset-WQKGOELE.js +26 -0
  322. package/vendor/framey/dist/audio-RSJWVWRA.js +1936 -0
  323. package/vendor/framey/dist/batchRender-3HPICDCT.js +366 -0
  324. package/vendor/framey/dist/beats-HL2BDUZI.js +273 -0
  325. package/vendor/framey/dist/benchmark-RWCWHI5Z.js +230 -0
  326. package/vendor/framey/dist/browser-ECYET3Q2.js +206 -0
  327. package/vendor/framey/dist/bundleWithLocalizedFonts-ALYBQEQT.js +52 -0
  328. package/vendor/framey/dist/captureCompositionFrame-4DA6SEH2.js +94 -0
  329. package/vendor/framey/dist/catalog-5JVDEUWK.js +979 -0
  330. package/vendor/framey/dist/catch-up-NEBCL5NO.js +106 -0
  331. package/vendor/framey/dist/check-F5N544ML.js +111 -0
  332. package/vendor/framey/dist/checkBrowser-LZ66CMJG.js +110 -0
  333. package/vendor/framey/dist/chunk-2K7Z2HVP.js +372 -0
  334. package/vendor/framey/dist/chunk-2UZQHDM7.js +323 -0
  335. package/vendor/framey/dist/chunk-2X4PQEYM.js +34 -0
  336. package/vendor/framey/dist/chunk-3422KMTD.js +168 -0
  337. package/vendor/framey/dist/chunk-3FYD4YEK.js +72 -0
  338. package/vendor/framey/dist/chunk-3GXQPCJC.js +682 -0
  339. package/vendor/framey/dist/chunk-3LM6YPBR.js +179 -0
  340. package/vendor/framey/dist/chunk-442JNXRM.js +20 -0
  341. package/vendor/framey/dist/chunk-4BNN5FGX.js +182 -0
  342. package/vendor/framey/dist/chunk-4P7NX5BM.js +11532 -0
  343. package/vendor/framey/dist/chunk-4UMPIPCQ.js +88 -0
  344. package/vendor/framey/dist/chunk-4YYJOFKS.js +28 -0
  345. package/vendor/framey/dist/chunk-54HJ63Q2.js +29 -0
  346. package/vendor/framey/dist/chunk-5ETJX6WM.js +89 -0
  347. package/vendor/framey/dist/chunk-5SG54JRS.js +68 -0
  348. package/vendor/framey/dist/chunk-634HSJ3J.js +58 -0
  349. package/vendor/framey/dist/chunk-66LREXGS.js +48 -0
  350. package/vendor/framey/dist/chunk-6BEFEWFB.js +25 -0
  351. package/vendor/framey/dist/chunk-6BNTGNGD.js +286 -0
  352. package/vendor/framey/dist/chunk-6DDPOHMK.js +140 -0
  353. package/vendor/framey/dist/chunk-6GV5XSOR.js +214 -0
  354. package/vendor/framey/dist/chunk-6JR2XUEE.js +127 -0
  355. package/vendor/framey/dist/chunk-73QVATIM.js +362 -0
  356. package/vendor/framey/dist/chunk-7FMUTGOH.js +279 -0
  357. package/vendor/framey/dist/chunk-7JQFJ6JJ.js +291 -0
  358. package/vendor/framey/dist/chunk-AHPGAESX.js +113 -0
  359. package/vendor/framey/dist/chunk-AHX34EFP.js +15 -0
  360. package/vendor/framey/dist/chunk-AJ5CSAEZ.js +487 -0
  361. package/vendor/framey/dist/chunk-B7RJV74O.js +5047 -0
  362. package/vendor/framey/dist/chunk-BL4U6U52.js +254 -0
  363. package/vendor/framey/dist/chunk-BSQ7XANM.js +210 -0
  364. package/vendor/framey/dist/chunk-BZ56MTDX.js +6 -0
  365. package/vendor/framey/dist/chunk-C5BO7PMT.js +282 -0
  366. package/vendor/framey/dist/chunk-CFUCHO4T.js +358 -0
  367. package/vendor/framey/dist/chunk-CZGHFATT.js +31 -0
  368. package/vendor/framey/dist/chunk-DLA44CMS.js +5649 -0
  369. package/vendor/framey/dist/chunk-DPVXBVLL.js +31292 -0
  370. package/vendor/framey/dist/chunk-DQBY7FRR.js +397 -0
  371. package/vendor/framey/dist/chunk-EAMLBAHA.js +154 -0
  372. package/vendor/framey/dist/chunk-EOVYQ3IG.js +143 -0
  373. package/vendor/framey/dist/chunk-EVMY7IPL.js +612 -0
  374. package/vendor/framey/dist/chunk-FG4BBFZI.js +122 -0
  375. package/vendor/framey/dist/chunk-FI4I7CME.js +56 -0
  376. package/vendor/framey/dist/chunk-FLREUEHW.js +2081 -0
  377. package/vendor/framey/dist/chunk-FUFP7A7S.js +1126 -0
  378. package/vendor/framey/dist/chunk-FV3PIKGH.js +56 -0
  379. package/vendor/framey/dist/chunk-FXMKHHPO.js +123 -0
  380. package/vendor/framey/dist/chunk-GGZAM5VH.js +1187 -0
  381. package/vendor/framey/dist/chunk-GKZEVJIB.js +39 -0
  382. package/vendor/framey/dist/chunk-GMECXFAN.js +57 -0
  383. package/vendor/framey/dist/chunk-GNEFDLFB.js +621 -0
  384. package/vendor/framey/dist/chunk-GPMLSOEG.js +19274 -0
  385. package/vendor/framey/dist/chunk-GPYVT5EE.js +59 -0
  386. package/vendor/framey/dist/chunk-H6IEMZEV.js +40 -0
  387. package/vendor/framey/dist/chunk-HEFQCIXR.js +170 -0
  388. package/vendor/framey/dist/chunk-HFQ4CWHA.js +118 -0
  389. package/vendor/framey/dist/chunk-HPDTCOKE.js +304 -0
  390. package/vendor/framey/dist/chunk-HTUHQKDB.js +548 -0
  391. package/vendor/framey/dist/chunk-HU2SPFNG.js +103 -0
  392. package/vendor/framey/dist/chunk-HVW7ABXD.js +19 -0
  393. package/vendor/framey/dist/chunk-IG7NDKXO.js +26 -0
  394. package/vendor/framey/dist/chunk-II3J3WXK.js +246 -0
  395. package/vendor/framey/dist/chunk-IR35BCLK.js +13 -0
  396. package/vendor/framey/dist/chunk-ISN5GRYI.js +20 -0
  397. package/vendor/framey/dist/chunk-IUONB55J.js +669 -0
  398. package/vendor/framey/dist/chunk-IVSUQR2B.js +161 -0
  399. package/vendor/framey/dist/chunk-IXU6PQSW.js +259 -0
  400. package/vendor/framey/dist/chunk-IZCW2NKT.js +29 -0
  401. package/vendor/framey/dist/chunk-JGXJU5PF.js +68 -0
  402. package/vendor/framey/dist/chunk-JHOCK3DG.js +27 -0
  403. package/vendor/framey/dist/chunk-JVBRJMOP.js +121 -0
  404. package/vendor/framey/dist/chunk-K4MJX7SR.js +17 -0
  405. package/vendor/framey/dist/chunk-K4WKGXRC.js +18 -0
  406. package/vendor/framey/dist/chunk-KGPLIH7B.js +127 -0
  407. package/vendor/framey/dist/chunk-KLIKCX6Q.js +63 -0
  408. package/vendor/framey/dist/chunk-KXTKKBOC.js +61 -0
  409. package/vendor/framey/dist/chunk-KZI44OPA.js +185 -0
  410. package/vendor/framey/dist/chunk-LEKZNRKI.js +45 -0
  411. package/vendor/framey/dist/chunk-LFGJWOVA.js +61 -0
  412. package/vendor/framey/dist/chunk-LLMSX6V6.js +17 -0
  413. package/vendor/framey/dist/chunk-LTGTN2TH.js +195 -0
  414. package/vendor/framey/dist/chunk-M2WBOQ2Q.js +54 -0
  415. package/vendor/framey/dist/chunk-MI5FTLYK.js +399 -0
  416. package/vendor/framey/dist/chunk-MLQP2QUF.js +1809 -0
  417. package/vendor/framey/dist/chunk-MMQ52NIZ.js +956 -0
  418. package/vendor/framey/dist/chunk-ND46XKK3.js +742 -0
  419. package/vendor/framey/dist/chunk-NYEXNSHM.js +504 -0
  420. package/vendor/framey/dist/chunk-O4YRB27L.js +57 -0
  421. package/vendor/framey/dist/chunk-O7BGSQ5F.js +92 -0
  422. package/vendor/framey/dist/chunk-OFQWTKPW.js +117 -0
  423. package/vendor/framey/dist/chunk-P5ZBLXJ6.js +91 -0
  424. package/vendor/framey/dist/chunk-P6LJ4Y32.js +4662 -0
  425. package/vendor/framey/dist/chunk-PKZ7RK5N.js +281 -0
  426. package/vendor/framey/dist/chunk-PURHAXFL.js +15 -0
  427. package/vendor/framey/dist/chunk-Q5X2B5WT.js +29 -0
  428. package/vendor/framey/dist/chunk-QAMKOG67.js +248 -0
  429. package/vendor/framey/dist/chunk-R6YBK4XX.js +514 -0
  430. package/vendor/framey/dist/chunk-RGLQURIX.js +2997 -0
  431. package/vendor/framey/dist/chunk-RNV3DM4S.js +3573 -0
  432. package/vendor/framey/dist/chunk-ROEW726S.js +4638 -0
  433. package/vendor/framey/dist/chunk-RQVDTPGD.js +6 -0
  434. package/vendor/framey/dist/chunk-RVZHGOGR.js +26 -0
  435. package/vendor/framey/dist/chunk-RXATE6FZ.js +385 -0
  436. package/vendor/framey/dist/chunk-S3S4TGEH.js +424 -0
  437. package/vendor/framey/dist/chunk-SDYDNHEE.js +11202 -0
  438. package/vendor/framey/dist/chunk-SSWBU67I.js +173 -0
  439. package/vendor/framey/dist/chunk-ST35C4K6.js +28 -0
  440. package/vendor/framey/dist/chunk-SZNTYTIV.js +990 -0
  441. package/vendor/framey/dist/chunk-T2EUWCJE.js +29 -0
  442. package/vendor/framey/dist/chunk-T6AGUTZJ.js +444 -0
  443. package/vendor/framey/dist/chunk-THJALHFJ.js +11131 -0
  444. package/vendor/framey/dist/chunk-TIQB63MX.js +18128 -0
  445. package/vendor/framey/dist/chunk-U242RTCT.js +134 -0
  446. package/vendor/framey/dist/chunk-UJZREAV6.js +3901 -0
  447. package/vendor/framey/dist/chunk-UMD3V7CU.js +264 -0
  448. package/vendor/framey/dist/chunk-UOFAE5RF.js +42 -0
  449. package/vendor/framey/dist/chunk-UTEIUGAZ.js +29 -0
  450. package/vendor/framey/dist/chunk-VAS6HQX3.js +138 -0
  451. package/vendor/framey/dist/chunk-VC76R6W2.js +27 -0
  452. package/vendor/framey/dist/chunk-VFZ7W5W7.js +226 -0
  453. package/vendor/framey/dist/chunk-VHNLZAE2.js +348 -0
  454. package/vendor/framey/dist/chunk-VMC4WSH5.js +278 -0
  455. package/vendor/framey/dist/chunk-VORQIH64.js +108 -0
  456. package/vendor/framey/dist/chunk-VTXFRIHO.js +186 -0
  457. package/vendor/framey/dist/chunk-W2LGJSEN.js +182 -0
  458. package/vendor/framey/dist/chunk-WKBM7NDG.js +32 -0
  459. package/vendor/framey/dist/chunk-WMQNIQDJ.js +2057 -0
  460. package/vendor/framey/dist/chunk-WN4XZC5F.js +504 -0
  461. package/vendor/framey/dist/chunk-WQ6ZXHFQ.js +133 -0
  462. package/vendor/framey/dist/chunk-X7HBSTCZ.js +53 -0
  463. package/vendor/framey/dist/chunk-X7MTZ4T2.js +6 -0
  464. package/vendor/framey/dist/chunk-XPTOJZJF.js +308 -0
  465. package/vendor/framey/dist/chunk-XT6MXC7A.js +6 -0
  466. package/vendor/framey/dist/chunk-XT7X6QCP.js +6 -0
  467. package/vendor/framey/dist/chunk-XTMDWNEC.js +2820 -0
  468. package/vendor/framey/dist/chunk-XUHOIDKG.js +6 -0
  469. package/vendor/framey/dist/chunk-XX7LTW5Y.js +138 -0
  470. package/vendor/framey/dist/chunk-Y3YGTZEG.js +382 -0
  471. package/vendor/framey/dist/chunk-Y5SBSGN7.js +346 -0
  472. package/vendor/framey/dist/chunk-YCMVI4MZ.js +625 -0
  473. package/vendor/framey/dist/chunk-YOR4CTCE.js +261 -0
  474. package/vendor/framey/dist/chunk-YPRBGITP.js +2748 -0
  475. package/vendor/framey/dist/chunk-ZKDMAOFU.js +32 -0
  476. package/vendor/framey/dist/chunk-ZW6DURPF.js +1318 -0
  477. package/vendor/framey/dist/clean-ZJOGDKDM.js +336 -0
  478. package/vendor/framey/dist/cli.js +439 -0
  479. package/vendor/framey/dist/cloudrun-HGJ3B6ZV.js +767 -0
  480. package/vendor/framey/dist/commands/contrast-audit.browser.js +643 -0
  481. package/vendor/framey/dist/commands/layout-audit.browser.js +2914 -0
  482. package/vendor/framey/dist/commands/motion-sample.browser.js +74 -0
  483. package/vendor/framey/dist/commands/motion-signature.browser.js +475 -0
  484. package/vendor/framey/dist/compare-W7UY3BAZ.js +463 -0
  485. package/vendor/framey/dist/compiler-TM3W6GYF.js +136 -0
  486. package/vendor/framey/dist/component-QF5VGYMU.js +176 -0
  487. package/vendor/framey/dist/compositions-XT3PMNDF.js +238 -0
  488. package/vendor/framey/dist/contactSheet-7W6GYHBQ.js +24 -0
  489. package/vendor/framey/dist/deploy-6FJPWKF2.js +299 -0
  490. package/vendor/framey/dist/destroy-HE2RIPKZ.js +57 -0
  491. package/vendor/framey/dist/deterministicFonts-RADD3BSX.js +50 -0
  492. package/vendor/framey/dist/dist-4S2FGGVY.js +422 -0
  493. package/vendor/framey/dist/dist-BE4B2EZE.js +32 -0
  494. package/vendor/framey/dist/dist-EWXJGN63.js +129 -0
  495. package/vendor/framey/dist/docs-VKIRFEEX.js +131 -0
  496. package/vendor/framey/dist/doctor-2UNJ2G7U.js +74 -0
  497. package/vendor/framey/dist/dom-37K6YOH4.js +14 -0
  498. package/vendor/framey/dist/dotEnv-7PZQTVIM.js +54 -0
  499. package/vendor/framey/dist/events-ACY3P2PA.js +76 -0
  500. package/vendor/framey/dist/events-IHB3B2MK.js +69 -0
  501. package/vendor/framey/dist/ffmpeg-DR5LGGSK.js +63 -0
  502. package/vendor/framey/dist/figma-BODVUPHB.js +63 -0
  503. package/vendor/framey/dist/fontData.generated-NXAOI4GH.js +13 -0
  504. package/vendor/framey/dist/fontLocalizeCli.js +142 -0
  505. package/vendor/framey/dist/gpuPolicy-XLEXORZY.js +49 -0
  506. package/vendor/framey/dist/grade-compare-GAXFQ74C.js +661 -0
  507. package/vendor/framey/dist/gsapParser-IEUSLYDJ.js +2885 -0
  508. package/vendor/framey/dist/gsapParserAcorn-LZJKCFQI.js +30 -0
  509. package/vendor/framey/dist/help-VCB5X2HS.js +184 -0
  510. package/vendor/framey/dist/history-ENGZEVFT.js +471 -0
  511. package/vendor/framey/dist/hyperframe.manifest.json +22 -0
  512. package/vendor/framey/dist/hyperframe.runtime.iife.js +1132 -0
  513. package/vendor/framey/dist/hyperframes-player.global.js +478 -0
  514. package/vendor/framey/dist/hyperframes-slideshow.global.js +148 -0
  515. package/vendor/framey/dist/info-WPCJHBMU.js +182 -0
  516. package/vendor/framey/dist/init-G4WGLYGD.js +1176 -0
  517. package/vendor/framey/dist/inspect-5IQ6CNEQ.js +106 -0
  518. package/vendor/framey/dist/installer-ML4HWUV7.js +40 -0
  519. package/vendor/framey/dist/keyframes-Q6XJO7VW.js +2755 -0
  520. package/vendor/framey/dist/lambda-A7JC74JF.js +468 -0
  521. package/vendor/framey/dist/layout-YET6XNXA.js +95 -0
  522. package/vendor/framey/dist/lint-I7FZN2VJ.js +162 -0
  523. package/vendor/framey/dist/lintProject-PDB3UN72.js +30 -0
  524. package/vendor/framey/dist/manager-CD6LJ7ZN.js +45 -0
  525. package/vendor/framey/dist/manager-WA4F2GST.js +67 -0
  526. package/vendor/framey/dist/media-treatment-BXYS4WVA.js +1030 -0
  527. package/vendor/framey/dist/media-use-LWH3MANK.js +113 -0
  528. package/vendor/framey/dist/models-K363FYM4.js +195 -0
  529. package/vendor/framey/dist/motionShot-GPAVWXLH.js +640 -0
  530. package/vendor/framey/dist/normalize-FASIPBUM.js +24 -0
  531. package/vendor/framey/dist/normalize-audio-VDWYRHN6.js +268 -0
  532. package/vendor/framey/dist/open-E3TB6P6J.js +128 -0
  533. package/vendor/framey/dist/parakeetRunner-UP34UOI2.js +58 -0
  534. package/vendor/framey/dist/pipeline-VXIUYEBU.js +64 -0
  535. package/vendor/framey/dist/play-BUTDQ4DV.js +353 -0
  536. package/vendor/framey/dist/policies-OLDYGFK5.js +309 -0
  537. package/vendor/framey/dist/preflight-2KZ2GWAZ.js +55 -0
  538. package/vendor/framey/dist/present-GXYSM7NX.js +353 -0
  539. package/vendor/framey/dist/preview-EKZ33JOJ.js +1910 -0
  540. package/vendor/framey/dist/producer-SKU5EV3J.js +13 -0
  541. package/vendor/framey/dist/progress-ZWB4Z4ET.js +76 -0
  542. package/vendor/framey/dist/projectConfig-3MZAYVTS.js +60 -0
  543. package/vendor/framey/dist/publication-DTKWNF74.js +33 -0
  544. package/vendor/framey/dist/remove-background-OY5L7XN2.js +265 -0
  545. package/vendor/framey/dist/render-CDV4GDFX.js +187 -0
  546. package/vendor/framey/dist/render-YE5YAMOD.js +2620 -0
  547. package/vendor/framey/dist/render-batch-ZAMFPCLL.js +274 -0
  548. package/vendor/framey/dist/renderSetupWorker.js +120 -0
  549. package/vendor/framey/dist/resolver-D5BIPSEW.js +42 -0
  550. package/vendor/framey/dist/runtimeVersion.js +18 -0
  551. package/vendor/framey/dist/sam-WFZXI4F7.js +23 -0
  552. package/vendor/framey/dist/shaderTransitionWorker.js +44 -0
  553. package/vendor/framey/dist/sherpa-RQWLV7I3.js +79 -0
  554. package/vendor/framey/dist/sherpaWorker.js +84 -0
  555. package/vendor/framey/dist/sites-AMOVABTK.js +54 -0
  556. package/vendor/framey/dist/skill-ZHVLH7KX.js +15 -0
  557. package/vendor/framey/dist/slideshow-SJGK4RQS.js +24 -0
  558. package/vendor/framey/dist/snapshot-TUXL6LBQ.js +1515 -0
  559. package/vendor/framey/dist/src-JHQGB2S6.js +215 -0
  560. package/vendor/framey/dist/src-Z5PGEMXB.js +532 -0
  561. package/vendor/framey/dist/state-B6H3WHCF.js +28 -0
  562. package/vendor/framey/dist/staticProjectServer-DFVZOUGI.js +63 -0
  563. package/vendor/framey/dist/studio/assets/hyperframes-player-DLaKjw0L.js +478 -0
  564. package/vendor/framey/dist/studio/assets/index-BX3KHhGX.js +71 -0
  565. package/vendor/framey/dist/studio/assets/index-CYbvOVp-.css +1 -0
  566. package/vendor/framey/dist/studio/assets/index-Dp3QEUGL.js +402 -0
  567. package/vendor/framey/dist/studio/assets/index-qrTAXbqc.js +531 -0
  568. package/vendor/framey/dist/studio/favicon.svg +14 -0
  569. package/vendor/framey/dist/studio/icons/timeline/audio.svg +7 -0
  570. package/vendor/framey/dist/studio/icons/timeline/captions.svg +5 -0
  571. package/vendor/framey/dist/studio/icons/timeline/composition.svg +12 -0
  572. package/vendor/framey/dist/studio/icons/timeline/image.svg +18 -0
  573. package/vendor/framey/dist/studio/icons/timeline/music.svg +10 -0
  574. package/vendor/framey/dist/studio/icons/timeline/text.svg +3 -0
  575. package/vendor/framey/dist/studio/index.html +23 -0
  576. package/vendor/framey/dist/studio-api-2NECGRMB.js +179 -0
  577. package/vendor/framey/dist/studioSelectionClient-IUUPKUHZ.js +25 -0
  578. package/vendor/framey/dist/studioServer-N2OPUMOQ.js +1569 -0
  579. package/vendor/framey/dist/synthesize-PKTB5ATG.js +183 -0
  580. package/vendor/framey/dist/system-7F3UEHQT.js +27 -0
  581. package/vendor/framey/dist/telemetry-DQGPEKM6.js +145 -0
  582. package/vendor/framey/dist/telemetry-UN55N6U6.js +77 -0
  583. package/vendor/framey/dist/timeline-LHUUMK63.js +1295 -0
  584. package/vendor/framey/dist/tokens-OLECWDFK.js +92 -0
  585. package/vendor/framey/dist/transcribe-O7HQKMIV.js +331 -0
  586. package/vendor/framey/dist/transcribe-UJESPBT5.js +76 -0
  587. package/vendor/framey/dist/tts-YCCQHZD4.js +234 -0
  588. package/vendor/framey/dist/urlDownloader-H2LXLG4U.js +48 -0
  589. package/vendor/framey/dist/usage-HG4WMM7A.js +2167 -0
  590. package/vendor/framey/dist/validate-F2XMN6EV.js +87 -0
  591. package/vendor/framey/dist/vendor/npm/gsap@3.15.0/GSAP-LICENSE.txt +8 -0
  592. package/vendor/framey/dist/vendor/npm/gsap@3.15.0/dist/CustomEase.min.js +11 -0
  593. package/vendor/framey/dist/vendor/npm/gsap@3.15.0/dist/MotionPathPlugin.min.js +11 -0
  594. package/vendor/framey/dist/vendor/npm/gsap@3.15.0/dist/gsap.min.js +11 -0
  595. package/vendor/framey/dist/warmCheck.js +220 -0
  596. package/vendor/framey/dist/warmCheckClient.js +139 -0
  597. package/vendor/framey/dist/workspaceBuildCheck-XYD2YTFW.js +133 -0
  598. package/vendor/framey-player/LICENSE +190 -0
  599. package/vendor/framey-player/NOTICE +16 -0
  600. package/vendor/framey-player/SOURCE.json +6 -0
  601. package/vendor/framey-player/hyperframes-player.global.js +478 -0
  602. package/vendor/framey.pin +1 -0
  603. package/bin/index.js +0 -3
@@ -0,0 +1,10 @@
1
+ import { redact } from "./domain/redact.js";
2
+ export function log(level, message, fields = {}) {
3
+ const line = redact(JSON.stringify({
4
+ ts: new Date().toISOString(),
5
+ level,
6
+ message,
7
+ ...fields,
8
+ }));
9
+ process.stdout.write(line + "\n");
10
+ }
@@ -0,0 +1,55 @@
1
+ import type { BusinessContact, CreatorSummary } from "./domain/creators.js";
2
+ export interface MailContext {
3
+ videoTitle?: string | null;
4
+ publicationUrl?: string | null;
5
+ }
6
+ /** Spec 018. `generic` is the original free-form draft. */
7
+ export type MailKind = "generic" | "sponsor" | "brand" | "creator_collab" | "client_update";
8
+ /** The only creator data a draft may use. Built from what the Creators service returns. */
9
+ export interface MailCreatorContext {
10
+ summary: CreatorSummary;
11
+ /** One recent top post. */
12
+ topPost?: {
13
+ title?: string;
14
+ url?: string;
15
+ } | null;
16
+ /** The published business contact. Never a bio, never a phone number. */
17
+ businessContact?: BusinessContact | null;
18
+ }
19
+ /** Who the message identifies itself as. Feeds the compliance footer. */
20
+ export interface MailSender {
21
+ senderName: string;
22
+ senderOrg: string;
23
+ postalAddress: string;
24
+ unsubscribeUrl: string;
25
+ }
26
+ /** What the operator asks for. This is not a pipeline event. */
27
+ export interface MailRequest {
28
+ accountId: string;
29
+ to: string;
30
+ need: string;
31
+ tone: string;
32
+ channelId?: string | null;
33
+ context?: MailContext | null;
34
+ /** Spec 018. Absent means `generic`. */
35
+ kind?: MailKind;
36
+ /** Spec 018. Personalisation for outreach kinds. */
37
+ creator?: MailCreatorContext | null;
38
+ /** Spec 018. Name of the person addressed, when known. */
39
+ recipientName?: string | null;
40
+ /** Spec 018. Required for outreach kinds. Appended as the compliance footer. */
41
+ sender?: MailSender | null;
42
+ }
43
+ export interface MailDraft {
44
+ accountId: string;
45
+ channelId: string | null;
46
+ to: string;
47
+ subject: string;
48
+ body: string;
49
+ text: string;
50
+ /** Spec 018. Escaped HTML twin of `text`. No tracking pixel, no remote image. */
51
+ html?: string;
52
+ /** Spec 018. */
53
+ kind?: MailKind;
54
+ status: "draft";
55
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,18 @@
1
+ {
2
+ "name": "@vfrey/contracts",
3
+ "version": "0.1.0",
4
+ "type": "module",
5
+ "description": "Shared types and pure rules for vfrey. No server.",
6
+ "engines": {
7
+ "node": ">=22.13"
8
+ },
9
+ "exports": {
10
+ ".": {
11
+ "types": "./dist/index.d.ts",
12
+ "import": "./dist/index.js"
13
+ }
14
+ },
15
+ "files": [
16
+ "dist"
17
+ ]
18
+ }
@@ -0,0 +1,41 @@
1
+ import type { Account, AppConfig, Candidate } from "@vfrey/contracts";
2
+ export { fingerprint, parseFeed, parseRedditListing, parseYouTubePopular } from "./parse.js";
3
+ /** The tags the scorer is known to emit. Any other non-empty tag is also treated as compliance. */
4
+ export declare const COMPLIANCE_TAGS: Set<string>;
5
+ export interface ScoreResult {
6
+ score: number;
7
+ safety: number;
8
+ reason: string;
9
+ riskClass: "ok" | "quality" | "compliance";
10
+ complianceTags: string[];
11
+ }
12
+ export interface SourceError {
13
+ source: string;
14
+ code: string;
15
+ message: string;
16
+ retryable: boolean;
17
+ retryAfterMs?: number;
18
+ }
19
+ export interface CollectInput {
20
+ config: AppConfig;
21
+ account: Account;
22
+ fetchImpl: typeof fetch;
23
+ manual: Candidate[];
24
+ }
25
+ export interface CollectResult {
26
+ candidates: Candidate[];
27
+ errors: SourceError[];
28
+ }
29
+ /**
30
+ * Collects every source on its own. One failing source never discards the
31
+ * others or the manual candidates; the failures come back in `errors`.
32
+ */
33
+ export declare function collectCandidatesDetailed(input: CollectInput): Promise<CollectResult>;
34
+ /**
35
+ * Returns the candidates. Failed sources are logged; when nothing at all was
36
+ * collected and a source failed, the first failure is rethrown so the worker
37
+ * can retry or stop. Use collectCandidatesDetailed to read the errors.
38
+ */
39
+ export declare function collectCandidates(input: CollectInput): Promise<Candidate[]>;
40
+ export declare function interpretScore(raw: unknown): ScoreResult;
41
+ export declare function assertScore(result: ScoreResult): void;
@@ -0,0 +1,221 @@
1
+ import { AppError, ComplianceError, ConfigError, RetryableError } from "@vfrey/contracts";
2
+ import { requestJson, retryAfterFrom } from "@vfrey/contracts";
3
+ import { log } from "@vfrey/contracts";
4
+ import { parseFeed, parseRedditListing, parseYouTubePopular } from "./parse.js";
5
+ export { fingerprint, parseFeed, parseRedditListing, parseYouTubePopular } from "./parse.js";
6
+ /** The tags the scorer is known to emit. Any other non-empty tag is also treated as compliance. */
7
+ export const COMPLIANCE_TAGS = new Set([
8
+ "medical_advice_persona",
9
+ "legal_advice_persona",
10
+ "financial_advice_persona",
11
+ "impersonation",
12
+ "minor_safety",
13
+ "election_manipulation",
14
+ "scam",
15
+ "copyrighted_transcript",
16
+ "graphic_shock_template",
17
+ ]);
18
+ const YOUTUBE_RETRY_REASONS = new Set(["quotaexceeded", "ratelimitexceeded", "userratelimitexceeded", "dailylimitexceeded"]);
19
+ function toSourceError(source, error) {
20
+ if (error instanceof AppError) {
21
+ const row = { source, code: error.code, message: error.message, retryable: error.retryable };
22
+ if (error instanceof RetryableError && error.retryAfterMs !== undefined)
23
+ row.retryAfterMs = error.retryAfterMs;
24
+ return row;
25
+ }
26
+ return { source, code: "source_failed", message: error instanceof Error ? error.message : String(error), retryable: false };
27
+ }
28
+ /**
29
+ * Collects every source on its own. One failing source never discards the
30
+ * others or the manual candidates; the failures come back in `errors`.
31
+ */
32
+ export async function collectCandidatesDetailed(input) {
33
+ const { candidates, errors } = await collectAll(input);
34
+ return { candidates, errors };
35
+ }
36
+ async function collectAll(input) {
37
+ const thrown = [];
38
+ const found = [...input.manual];
39
+ const errors = [];
40
+ const guard = async (source, run) => {
41
+ try {
42
+ await run();
43
+ }
44
+ catch (error) {
45
+ thrown.push(error);
46
+ errors.push(toSourceError(source, error));
47
+ }
48
+ };
49
+ for (const url of input.account.sources.rss) {
50
+ if (!isHttpSource(url)) {
51
+ log("warn", "source url rejected", { accountId: input.account.id, code: "source_url_rejected", url });
52
+ continue;
53
+ }
54
+ await guard(`rss:${url}`, async () => {
55
+ const result = await requestJson(input.fetchImpl, url, { method: "GET", timeoutMs: 20_000 });
56
+ if (result.status >= 500)
57
+ throw new RetryableError("rss", `RSS ${result.status} ${url}`);
58
+ if (result.status < 400)
59
+ found.push(...parseFeed(result.text, "rss"));
60
+ });
61
+ }
62
+ for (const channelId of input.account.sources.youtubeChannelIds) {
63
+ await guard(`youtube-channel:${channelId}`, async () => {
64
+ const url = `https://www.youtube.com/feeds/videos.xml?channel_id=${encodeURIComponent(channelId)}`;
65
+ const result = await requestJson(input.fetchImpl, url, { method: "GET" });
66
+ if (result.status < 400)
67
+ found.push(...parseFeed(result.text, "youtube"));
68
+ });
69
+ }
70
+ if (input.account.sources.subreddits.length > 0) {
71
+ let token = null;
72
+ await guard("reddit:token", async () => {
73
+ token = await redditToken(input.config, input.fetchImpl);
74
+ });
75
+ if (token !== null) {
76
+ for (const sub of input.account.sources.subreddits) {
77
+ await guard(`reddit:${sub}`, async () => {
78
+ const result = await requestJson(input.fetchImpl, `https://oauth.reddit.com/r/${encodeURIComponent(sub)}/hot?limit=15`, {
79
+ method: "GET",
80
+ headers: {
81
+ authorization: `Bearer ${token}`,
82
+ "user-agent": input.config.reddit.userAgent,
83
+ },
84
+ });
85
+ if (result.status === 429) {
86
+ throw new RetryableError("reddit_rate", "Reddit rate limit", retryAfterFrom(result, Date.now()));
87
+ }
88
+ if (result.status < 400 && result.json)
89
+ found.push(...parseRedditListing(result.json));
90
+ });
91
+ }
92
+ }
93
+ }
94
+ if (input.account.sources.youtubeTrendingRegion && input.config.googleApiKey) {
95
+ const region = input.account.sources.youtubeTrendingRegion;
96
+ const apiKey = input.config.googleApiKey;
97
+ await guard("youtube-trending", async () => {
98
+ const url = `https://www.googleapis.com/youtube/v3/videos?part=snippet&chart=mostPopular&regionCode=${encodeURIComponent(region)}` +
99
+ `&maxResults=10&key=${encodeURIComponent(apiKey)}`;
100
+ const result = await requestJson(input.fetchImpl, url, { method: "GET" });
101
+ if (result.status === 400 || result.status === 403 || result.status === 429) {
102
+ const reason = youtubeReason(result.json);
103
+ if (YOUTUBE_RETRY_REASONS.has(reason) || (result.status === 429 && reason === "")) {
104
+ // Quota is a transport pause, not a reason to scrape an unofficial endpoint.
105
+ throw new RetryableError("youtube_quota", "YouTube Data API quota blocked trending lookup", 60 * 60_000);
106
+ }
107
+ if (result.status !== 429) {
108
+ throw new ConfigError("youtube_key", `YouTube Data API refused the key (${result.status} ${reason || "no reason"})`);
109
+ }
110
+ }
111
+ if (result.status < 400 && result.json)
112
+ found.push(...parseYouTubePopular(result.json));
113
+ });
114
+ }
115
+ // TikTok has no official public trend endpoint for this app class.
116
+ // video.list on the owner's account is an analytics signal, not a topic feed,
117
+ // and third-party footage is never downloaded. See the vfreyai/vfrey-pipeline docs.
118
+ const deduped = new Map();
119
+ // First wins, and manual candidates are first, so a feed item never replaces one.
120
+ for (const candidate of found)
121
+ if (!deduped.has(candidate.fingerprint))
122
+ deduped.set(candidate.fingerprint, candidate);
123
+ return { candidates: [...deduped.values()].slice(0, 30), errors, thrown };
124
+ }
125
+ /**
126
+ * Returns the candidates. Failed sources are logged; when nothing at all was
127
+ * collected and a source failed, the first failure is rethrown so the worker
128
+ * can retry or stop. Use collectCandidatesDetailed to read the errors.
129
+ */
130
+ export async function collectCandidates(input) {
131
+ const result = await collectAll(input);
132
+ for (const error of result.errors) {
133
+ log("warn", "source failed", { accountId: input.account.id, code: error.code, source: error.source });
134
+ }
135
+ if (result.candidates.length === 0 && result.errors.length > 0)
136
+ throw result.thrown[0];
137
+ return result.candidates;
138
+ }
139
+ function isHttpSource(url) {
140
+ try {
141
+ const protocol = new URL(url).protocol;
142
+ return protocol === "http:" || protocol === "https:";
143
+ }
144
+ catch {
145
+ return false;
146
+ }
147
+ }
148
+ function youtubeReason(json) {
149
+ const error = json?.error;
150
+ const reason = error?.errors?.[0]?.reason;
151
+ return typeof reason === "string" ? reason.toLowerCase() : "";
152
+ }
153
+ async function redditToken(config, fetchImpl) {
154
+ if (!config.reddit.clientId || !config.reddit.clientSecret) {
155
+ throw new ConfigError("reddit_auth", "REDDIT_CLIENT_ID and REDDIT_CLIENT_SECRET are required for subreddit sources");
156
+ }
157
+ if (!config.reddit.userAgent || config.reddit.userAgent.trim() === "") {
158
+ throw new ConfigError("reddit_user_agent", "A non-empty Reddit User-Agent is required for subreddit sources");
159
+ }
160
+ const basic = Buffer.from(`${config.reddit.clientId}:${config.reddit.clientSecret}`).toString("base64");
161
+ const result = await requestJson(fetchImpl, "https://www.reddit.com/api/v1/access_token", {
162
+ method: "POST",
163
+ headers: {
164
+ authorization: `Basic ${basic}`,
165
+ "content-type": "application/x-www-form-urlencoded",
166
+ "user-agent": config.reddit.userAgent,
167
+ },
168
+ body: "grant_type=client_credentials",
169
+ });
170
+ if (result.status === 401 || result.status === 403) {
171
+ throw new ConfigError("reddit_auth", `Reddit token HTTP ${result.status}: check the client id and secret`);
172
+ }
173
+ if (result.status >= 400) {
174
+ throw new RetryableError("reddit_auth", `Reddit token HTTP ${result.status}`, retryAfterFrom(result, Date.now()));
175
+ }
176
+ const token = result.json?.access_token;
177
+ if (!token)
178
+ throw new RetryableError("reddit_auth", "Reddit token response had no access_token");
179
+ return token;
180
+ }
181
+ /** Placeholders a model emits to mean "no tags". They are not tags. */
182
+ const NO_TAG_SENTINELS = new Set(["none", "n_a", "na", "null", "nil", "ok", "safe", "no", "false", "empty"]);
183
+ function normalizeTag(tag) {
184
+ return tag
185
+ .normalize("NFKC")
186
+ .toLowerCase()
187
+ .replace(/[^a-z0-9]+/g, "_")
188
+ .replace(/^_+|_+$/g, "");
189
+ }
190
+ export function interpretScore(raw) {
191
+ const row = (raw ?? {});
192
+ const tags = Array.isArray(row.complianceTags) ? row.complianceTags.map(String) : [];
193
+ const score = clamp(row.score);
194
+ const safety = clamp(row.safety);
195
+ let riskClass = row.riskClass === "compliance" || row.riskClass === "quality" ? row.riskClass : "ok";
196
+ // Compliance only escalates. A quality verdict never overwrites it.
197
+ // An unrecognized non-empty tag fails closed as compliance.
198
+ const flagged = tags.map(normalizeTag).some((tag) => tag !== "" && !NO_TAG_SENTINELS.has(tag));
199
+ if (riskClass !== "compliance") {
200
+ if (flagged || safety < 15)
201
+ riskClass = "compliance";
202
+ else if (score < 70)
203
+ riskClass = "quality";
204
+ }
205
+ return {
206
+ score,
207
+ safety,
208
+ reason: String(row.reason ?? ""),
209
+ riskClass,
210
+ complianceTags: tags,
211
+ };
212
+ }
213
+ function clamp(value) {
214
+ const number = typeof value === "number" ? value : 0;
215
+ return Math.max(0, Math.min(100, number));
216
+ }
217
+ export function assertScore(result) {
218
+ if (result.riskClass === "compliance") {
219
+ throw new ComplianceError("source_compliance", result.reason || result.complianceTags.join(","));
220
+ }
221
+ }
@@ -0,0 +1,5 @@
1
+ import type { Candidate } from "@vfrey/contracts";
2
+ export declare function fingerprint(provider: string, url: string, title: string): string;
3
+ export declare function parseFeed(xml: string, provider: Candidate["provider"]): Candidate[];
4
+ export declare function parseRedditListing(json: unknown): Candidate[];
5
+ export declare function parseYouTubePopular(json: unknown): Candidate[];
@@ -0,0 +1,165 @@
1
+ import { createHash } from "node:crypto";
2
+ const TRACKING_PARAM = /^(utm_[^=]*|fbclid|gclid)$/i;
3
+ /** Lowercase scheme and host only, drop tracking params, keep path and query case. */
4
+ function normalizeUrl(url) {
5
+ let parsed;
6
+ try {
7
+ parsed = new URL(url);
8
+ }
9
+ catch {
10
+ return url;
11
+ }
12
+ const kept = parsed.search
13
+ .replace(/^\?/, "")
14
+ .split("&")
15
+ .filter((pair) => pair !== "" && !TRACKING_PARAM.test(pair.split("=")[0] ?? ""));
16
+ const search = kept.length > 0 ? `?${kept.join("&")}` : "";
17
+ return `${parsed.protocol}//${parsed.host}${parsed.pathname}${search}${parsed.hash}`;
18
+ }
19
+ export function fingerprint(provider, url, title) {
20
+ const key = `${provider.toLowerCase()}|${normalizeUrl(url)}|${title.toLowerCase()}`;
21
+ return createHash("sha256").update(key).digest("hex").slice(0, 24);
22
+ }
23
+ const NAMED_ENTITIES = {
24
+ amp: "&",
25
+ lt: "<",
26
+ gt: ">",
27
+ quot: '"',
28
+ apos: "'",
29
+ nbsp: " ",
30
+ rsquo: "\u2019",
31
+ lsquo: "\u2018",
32
+ rdquo: "\u201d",
33
+ ldquo: "\u201c",
34
+ ndash: "\u2013",
35
+ mdash: "\u2014",
36
+ hellip: "\u2026",
37
+ };
38
+ function codePoint(value, original) {
39
+ if (!Number.isInteger(value) || value < 0 || value > 0x10ffff)
40
+ return original;
41
+ try {
42
+ return String.fromCodePoint(value);
43
+ }
44
+ catch {
45
+ return original;
46
+ }
47
+ }
48
+ function decodeEntities(value) {
49
+ return value.replace(/&(#x[0-9a-f]+|#[0-9]+|[a-z]+);/gi, (whole, body) => {
50
+ if (body[0] === "#") {
51
+ const hex = body[1] === "x" || body[1] === "X";
52
+ return codePoint(parseInt(body.slice(hex ? 2 : 1), hex ? 16 : 10), whole);
53
+ }
54
+ return NAMED_ENTITIES[body.toLowerCase()] ?? whole;
55
+ });
56
+ }
57
+ function decode(value) {
58
+ return decodeEntities(value.replace(/<!\[CDATA\[([\s\S]*?)\]\]>/g, "$1"))
59
+ .replace(/<[^>]+>/g, " ")
60
+ .replace(/\s+/g, " ")
61
+ .trim();
62
+ }
63
+ function tag(block, name) {
64
+ const match = new RegExp(`<${name}(?:\\s[^>]*)?>([\\s\\S]*?)</${name}>`, "i").exec(block);
65
+ return match ? decode(match[1] ?? "") : "";
66
+ }
67
+ function isHttpUrl(value) {
68
+ try {
69
+ const protocol = new URL(value).protocol;
70
+ return protocol === "http:" || protocol === "https:";
71
+ }
72
+ catch {
73
+ return false;
74
+ }
75
+ }
76
+ function attrOf(tagText, name) {
77
+ const match = new RegExp(`\\s${name}\\s*=\\s*(?:"([^"]*)"|'([^']*)')`, "i").exec(tagText);
78
+ return match?.[1] ?? match?.[2] ?? "";
79
+ }
80
+ /** Atom links: prefer rel="alternate" (or no rel) over replies, enclosures, and the like. */
81
+ function atomLink(block) {
82
+ for (const match of block.matchAll(/<link\b[^>]*>/gi)) {
83
+ const href = attrOf(match[0], "href");
84
+ const rel = attrOf(match[0], "rel").toLowerCase();
85
+ if (href && (rel === "" || rel === "alternate"))
86
+ return decodeEntities(href);
87
+ }
88
+ return "";
89
+ }
90
+ function isoDate(value) {
91
+ if (!value)
92
+ return null;
93
+ const time = Date.parse(value);
94
+ return Number.isFinite(time) ? new Date(time).toISOString() : null;
95
+ }
96
+ export function parseFeed(xml, provider) {
97
+ const blocks = xml.split(/<item\b|<entry\b/i).slice(1);
98
+ const candidates = [];
99
+ for (const block of blocks) {
100
+ const title = tag(block, "title");
101
+ const link = atomLink(block) || tag(block, "link") || tag(block, "id");
102
+ const summary = tag(block, "description") ||
103
+ tag(block, "summary") ||
104
+ tag(block, "content:encoded") ||
105
+ tag(block, "content") ||
106
+ tag(block, "media:description");
107
+ const published = tag(block, "pubDate") || tag(block, "published") || tag(block, "updated");
108
+ if (!title || !link || !isHttpUrl(link))
109
+ continue;
110
+ candidates.push({
111
+ fingerprint: fingerprint(provider, link, title),
112
+ provider,
113
+ title: title.slice(0, 300),
114
+ url: link.slice(0, 500),
115
+ excerpt: summary.slice(0, 500),
116
+ publishedAt: isoDate(published),
117
+ });
118
+ }
119
+ return candidates;
120
+ }
121
+ export function parseRedditListing(json) {
122
+ const data = json;
123
+ const children = data.data?.children ?? [];
124
+ const candidates = [];
125
+ for (const child of children) {
126
+ const row = child.data ?? {};
127
+ if (row.stickied === true || row.over_18 === true)
128
+ continue;
129
+ const title = String(row.title ?? "");
130
+ const permalink = String(row.permalink ?? "");
131
+ if (!title || !permalink)
132
+ continue;
133
+ const url = `https://www.reddit.com${permalink}`;
134
+ const selftext = String(row.selftext ?? "");
135
+ candidates.push({
136
+ fingerprint: fingerprint("reddit", url, title),
137
+ provider: "reddit",
138
+ title: title.slice(0, 300),
139
+ url,
140
+ excerpt: selftext.slice(0, 500),
141
+ publishedAt: typeof row.created_utc === "number" ? new Date(row.created_utc * 1000).toISOString() : null,
142
+ });
143
+ }
144
+ return candidates;
145
+ }
146
+ export function parseYouTubePopular(json) {
147
+ const data = json;
148
+ const candidates = [];
149
+ for (const item of data.items ?? []) {
150
+ const id = item.id ?? "";
151
+ const title = String(item.snippet?.title ?? "");
152
+ if (!id || !title)
153
+ continue;
154
+ const url = `https://www.youtube.com/watch?v=${id}`;
155
+ candidates.push({
156
+ fingerprint: fingerprint("youtube", url, title),
157
+ provider: "youtube",
158
+ title: title.slice(0, 300),
159
+ url,
160
+ excerpt: String(item.snippet?.description ?? "").slice(0, 500),
161
+ publishedAt: typeof item.snippet?.publishedAt === "string" ? item.snippet.publishedAt : null,
162
+ });
163
+ }
164
+ return candidates;
165
+ }
@@ -0,0 +1,21 @@
1
+ {
2
+ "name": "@vfrey/ingest",
3
+ "version": "0.1.0",
4
+ "type": "module",
5
+ "description": "Official RSS, YouTube, and Reddit collectors for vfrey. No upload tokens.",
6
+ "engines": {
7
+ "node": ">=22.13"
8
+ },
9
+ "exports": {
10
+ ".": {
11
+ "types": "./dist/index.d.ts",
12
+ "import": "./dist/index.js"
13
+ }
14
+ },
15
+ "files": [
16
+ "dist"
17
+ ],
18
+ "dependencies": {
19
+ "@vfrey/contracts": "file:../contracts"
20
+ }
21
+ }