@gentbajko/slopify 3.25.9 → 4.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (603) hide show
  1. package/dist/adapters/account-limits.js +30 -0
  2. package/dist/adapters/alignment/cache.js +3 -5
  3. package/dist/adapters/alignment/ctc.js +1 -2
  4. package/dist/adapters/alignment/lock.js +4 -3
  5. package/dist/adapters/alignment/multilingual.js +0 -3
  6. package/dist/adapters/alignment/prefetch.js +2 -1
  7. package/dist/adapters/alignment/worker.js +2 -1
  8. package/dist/adapters/explain.js +0 -3
  9. package/dist/adapters/ffmpeg.js +19 -1
  10. package/dist/adapters/host-cli/index.js +19 -3
  11. package/dist/adapters/http-failure.js +53 -0
  12. package/dist/adapters/image/codex.js +36 -4
  13. package/dist/adapters/image/fal.js +17 -50
  14. package/dist/adapters/image/google.js +31 -55
  15. package/dist/adapters/image/openai.js +20 -47
  16. package/dist/adapters/image/reference.js +36 -0
  17. package/dist/adapters/image/replicate.js +18 -47
  18. package/dist/adapters/interest/search-interest.js +113 -0
  19. package/dist/adapters/key-probes.js +53 -2
  20. package/dist/adapters/llm/antigravity.js +4 -0
  21. package/dist/adapters/llm/assistant-cli.js +388 -0
  22. package/dist/adapters/llm/child-session.js +83 -0
  23. package/dist/adapters/llm/claude-code-models.js +51 -98
  24. package/dist/adapters/llm/claude-code.js +13 -2
  25. package/dist/adapters/llm/codex-limits.js +27 -68
  26. package/dist/adapters/llm/codex-server-models.js +61 -109
  27. package/dist/adapters/llm/codex.js +12 -1
  28. package/dist/adapters/llm/openrouter.js +10 -47
  29. package/dist/adapters/llm/run-cli.js +6 -22
  30. package/dist/adapters/llm/sse-lines.js +5 -1
  31. package/dist/adapters/model-access.js +37 -0
  32. package/dist/adapters/tts/cartesia.js +15 -46
  33. package/dist/adapters/tts/elevenlabs.js +20 -52
  34. package/dist/adapters/tts/gemini.js +19 -40
  35. package/dist/adapters/tts/inworld-async.js +4 -1
  36. package/dist/adapters/tts/inworld.js +3 -0
  37. package/dist/adapters/tts/openai.js +20 -50
  38. package/dist/assets/models-lifecycle.yaml +6 -0
  39. package/dist/assistant-wiring.js +60 -0
  40. package/dist/autopilot-timers.js +59 -0
  41. package/dist/catalog/lifecycle.js +52 -0
  42. package/dist/catalog/propose.js +204 -0
  43. package/dist/catalog/registry.js +3 -1
  44. package/dist/catalog/schema.js +2 -0
  45. package/dist/catalog/store.js +39 -0
  46. package/dist/edge/autostart/native.js +3 -5
  47. package/dist/edge/autostart/prompt.js +2 -1
  48. package/dist/edge/autostart/service.js +3 -4
  49. package/dist/edge/cli-args.js +3 -4
  50. package/dist/edge/cli.js +2 -1
  51. package/dist/edge/docker-install/apply.js +11 -11
  52. package/dist/edge/docker-install/run.js +2 -1
  53. package/dist/edge/docker-install/state.js +4 -3
  54. package/dist/edge/docker-install/tree.js +2 -4
  55. package/dist/edge/docker-install/volume.js +3 -2
  56. package/dist/edge/docker.js +5 -4
  57. package/dist/edge/events/hub.js +5 -5
  58. package/dist/edge/http/actions.js +4 -25
  59. package/dist/edge/http/actor.js +5 -3
  60. package/dist/edge/http/app.js +85 -10
  61. package/dist/edge/http/assistant.js +127 -0
  62. package/dist/edge/http/audio-preview.js +4 -11
  63. package/dist/edge/http/auditions.js +1 -1
  64. package/dist/edge/http/autostart.js +5 -5
  65. package/dist/edge/http/backups.js +1 -3
  66. package/dist/edge/http/channel-memory.js +7 -20
  67. package/dist/edge/http/channels.js +13 -26
  68. package/dist/edge/http/checkpoints.js +33 -17
  69. package/dist/edge/http/design-templates.js +167 -0
  70. package/dist/edge/http/designs.js +178 -0
  71. package/dist/edge/http/diagnostics.js +5 -1
  72. package/dist/edge/http/document-themes.js +2 -11
  73. package/dist/edge/http/draft-files.js +1 -3
  74. package/dist/edge/http/draft-problem.js +4 -9
  75. package/dist/edge/http/drafts.js +3 -3
  76. package/dist/edge/http/entries.js +6 -9
  77. package/dist/edge/http/extension-auth.js +27 -0
  78. package/dist/edge/http/external-reviews.js +12 -11
  79. package/dist/edge/http/files.js +5 -11
  80. package/dist/edge/http/folder-location.js +1 -4
  81. package/dist/edge/http/fonts.js +0 -7
  82. package/dist/edge/http/home.js +38 -13
  83. package/dist/edge/http/host-cli.js +18 -8
  84. package/dist/edge/http/ids.js +8 -0
  85. package/dist/edge/http/keywords.js +35 -0
  86. package/dist/edge/http/model-checks.js +132 -0
  87. package/dist/edge/http/narration-peaks.js +5 -13
  88. package/dist/edge/http/onboarding.js +1 -14
  89. package/dist/edge/http/open-folder.js +5 -10
  90. package/dist/edge/http/outputs.js +4 -12
  91. package/dist/edge/http/patch-notes.js +4 -10
  92. package/dist/edge/http/planning.js +2 -6
  93. package/dist/edge/http/problem.js +2 -4
  94. package/dist/edge/http/project-create.js +1 -0
  95. package/dist/edge/http/project-templates.js +29 -17
  96. package/dist/edge/http/projects.js +2 -12
  97. package/dist/edge/http/prompts.js +7 -14
  98. package/dist/edge/http/pronunciations.js +1 -2
  99. package/dist/edge/http/providers.js +39 -24
  100. package/dist/edge/http/refusal.js +29 -0
  101. package/dist/edge/http/remote-access.js +40 -0
  102. package/dist/edge/http/remote-guard.js +71 -0
  103. package/dist/edge/http/request-guard.js +91 -0
  104. package/dist/edge/http/reviews.js +11 -24
  105. package/dist/edge/http/revision-files.js +5 -10
  106. package/dist/edge/http/revisions.js +10 -15
  107. package/dist/edge/http/run-cost.js +1 -2
  108. package/dist/edge/http/same-page.js +80 -0
  109. package/dist/edge/http/schedules.js +59 -32
  110. package/dist/edge/http/settings.js +22 -13
  111. package/dist/edge/http/social.js +282 -0
  112. package/dist/edge/http/staging.js +2 -4
  113. package/dist/edge/http/storage-files.js +5 -7
  114. package/dist/edge/http/storage.js +23 -23
  115. package/dist/edge/http/studio.js +23 -50
  116. package/dist/edge/http/style-preview.js +1 -4
  117. package/dist/edge/http/subtitles.js +3 -17
  118. package/dist/edge/http/template-studio.js +433 -0
  119. package/dist/edge/http/trash.js +10 -11
  120. package/dist/edge/http/tutorial.js +1 -2
  121. package/dist/edge/http/tutorials.js +5 -11
  122. package/dist/edge/http/update.js +7 -13
  123. package/dist/edge/http/waveform.js +3 -6
  124. package/dist/edge/http/youtube-edits.js +2 -3
  125. package/dist/edge/mcp/assistant.js +279 -0
  126. package/dist/edge/mcp/attribution.js +8 -2
  127. package/dist/edge/mcp/call-route.js +11 -2
  128. package/dist/edge/mcp/excluded-routes.js +25 -0
  129. package/dist/edge/mcp/http.js +11 -2
  130. package/dist/edge/mcp/instructions.js +5 -3
  131. package/dist/edge/mcp/library-tools.js +1 -0
  132. package/dist/edge/mcp/route-tool.js +35 -15
  133. package/dist/edge/mcp/server.js +13 -8
  134. package/dist/edge/mcp/tools-channels.js +2 -0
  135. package/dist/edge/mcp/tools-designs.js +165 -0
  136. package/dist/edge/mcp/tools-library.js +14 -2
  137. package/dist/edge/mcp/tools-planning.js +23 -4
  138. package/dist/edge/mcp/tools-projects.js +28 -1
  139. package/dist/edge/mcp/tools-runs.js +14 -1
  140. package/dist/edge/mcp/tools-schedules.js +28 -4
  141. package/dist/edge/mcp/tools-setup.js +61 -3
  142. package/dist/edge/mcp/tools-social.js +46 -0
  143. package/dist/edge/mcp/tools-storage.js +8 -0
  144. package/dist/edge/mcp/tools-template-studio.js +166 -0
  145. package/dist/edge/mcp/tools.js +6 -0
  146. package/dist/edge/remote-access/cli.js +71 -0
  147. package/dist/edge/remote-access/model.js +32 -0
  148. package/dist/edge/remote-access/rules.js +207 -0
  149. package/dist/edge/remote-access/service.js +203 -0
  150. package/dist/edge/remote-access/store.js +44 -0
  151. package/dist/edge/remote-access/views.js +43 -0
  152. package/dist/extension/slopify-studio-chrome.zip +0 -0
  153. package/dist/extension/slopify-studio-firefox.zip +0 -0
  154. package/dist/host-cli/install.js +2 -2
  155. package/dist/host-cli/open-folder.js +2 -2
  156. package/dist/host-cli/paths.js +2 -4
  157. package/dist/host-cli/server.js +4 -4
  158. package/dist/host-cli/service.js +3 -3
  159. package/dist/host-cli/status.js +7 -7
  160. package/dist/kernel/db/migrations/0063-model-checks.sql +20 -0
  161. package/dist/kernel/db/migrations/0064-template-studio.sql +46 -0
  162. package/dist/kernel/db/migrations/0065-promise-and-assistant-reviews.sql +58 -0
  163. package/dist/kernel/db/migrations/0066-assistant.sql +44 -0
  164. package/dist/kernel/db/migrations/0067-autopilot.sql +6 -0
  165. package/dist/kernel/db/migrations/0068-topic-scores.sql +17 -0
  166. package/dist/kernel/db/migrations/0070-designs.sql +53 -0
  167. package/dist/kernel/db/migrations/0071-design-usage.sql +33 -0
  168. package/dist/kernel/db/migrations/0072-design-templates.sql +33 -0
  169. package/dist/kernel/db/migrations/0073-schedule-design-kind.sql +5 -0
  170. package/dist/kernel/db/migrations/0074-template-bakeoffs.sql +44 -0
  171. package/dist/kernel/db/migrations/0075-social-posts.sql +25 -0
  172. package/dist/kernel/db/migrations/0076-channel-social.sql +5 -0
  173. package/dist/kernel/db/migrations/0077-post-prompts.sql +17 -0
  174. package/dist/kernel/db/tx.js +2 -4
  175. package/dist/kernel/errors.js +27 -1
  176. package/dist/kernel/json-answer.js +15 -0
  177. package/dist/kernel/lock.js +4 -9
  178. package/dist/kernel/paths.js +8 -0
  179. package/dist/kernel/ports/assistant-cli.js +4 -0
  180. package/dist/kernel/ports/host-cli.js +3 -0
  181. package/dist/kernel/ports/interest.js +4 -0
  182. package/dist/kernel/ports/languages.js +0 -3
  183. package/dist/kernel/ports/model.js +2 -0
  184. package/dist/kernel/runner/attempt.js +3 -1
  185. package/dist/kernel/runner/audition.js +3 -1
  186. package/dist/kernel/runner/checkpoint-authority.js +2 -1
  187. package/dist/kernel/runner/index.js +8 -10
  188. package/dist/kernel/runner/model-access.js +40 -0
  189. package/dist/kernel/runner/model-check.js +153 -0
  190. package/dist/kernel/runner/providers.js +15 -10
  191. package/dist/kernel/runner/standalone.js +9 -5
  192. package/dist/main.js +334 -19
  193. package/dist/model-checks.js +166 -0
  194. package/dist/patch-notes/4.0.0.md +51 -0
  195. package/dist/patch-notes/index.json +6 -0
  196. package/dist/slices/admission/heads.js +2 -1
  197. package/dist/slices/admission/repo.js +2 -1
  198. package/dist/slices/admission/rules.js +43 -8
  199. package/dist/slices/admission/schema.js +8 -0
  200. package/dist/slices/admission/stage-writes.js +0 -3
  201. package/dist/slices/admission/start.js +48 -31
  202. package/dist/slices/admission/substitute.js +7 -4
  203. package/dist/slices/agents/attention-posts.js +31 -0
  204. package/dist/slices/agents/attention-templates.js +33 -0
  205. package/dist/slices/agents/attention.js +10 -1
  206. package/dist/slices/agents/connection.js +2 -6
  207. package/dist/slices/agents/model.js +6 -3
  208. package/dist/slices/agents/repo.js +15 -7
  209. package/dist/slices/article/continuation.js +0 -63
  210. package/dist/slices/article/plain.js +3 -2
  211. package/dist/slices/article/segments.js +2 -62
  212. package/dist/slices/assistant/access.js +1 -0
  213. package/dist/slices/assistant/asks.js +80 -0
  214. package/dist/slices/assistant/cost.js +54 -0
  215. package/dist/slices/assistant/gate.js +222 -0
  216. package/dist/slices/assistant/model.js +79 -0
  217. package/dist/slices/assistant/names.js +23 -0
  218. package/dist/slices/assistant/prompt.js +74 -0
  219. package/dist/slices/assistant/repo.js +74 -0
  220. package/dist/slices/assistant/tokens.js +18 -0
  221. package/dist/slices/assistant/turn.js +342 -0
  222. package/dist/slices/autopilot/buffer.js +76 -0
  223. package/dist/slices/autopilot/model.js +14 -0
  224. package/dist/slices/autopilot/notify.js +27 -0
  225. package/dist/slices/autopilot/repo.js +34 -0
  226. package/dist/slices/autopilot/signals.js +194 -0
  227. package/dist/slices/autopilot/warnings.js +85 -0
  228. package/dist/slices/autopilot/weekly.js +100 -0
  229. package/dist/slices/backups/files.js +2 -6
  230. package/dist/slices/backups/repo.js +3 -15
  231. package/dist/slices/backups/service.js +4 -3
  232. package/dist/slices/channels/cast-images.js +2 -1
  233. package/dist/slices/channels/model.js +0 -1
  234. package/dist/slices/channels/rebrand.js +0 -3
  235. package/dist/slices/channels/repo.js +4 -0
  236. package/dist/slices/channels/schema.js +6 -0
  237. package/dist/slices/channels/service.js +10 -0
  238. package/dist/slices/channels/videos.js +2 -14
  239. package/dist/slices/checkpoints/index.js +0 -1
  240. package/dist/slices/control/providers.js +0 -26
  241. package/dist/slices/designs/artwork.js +66 -0
  242. package/dist/slices/designs/budget.js +23 -0
  243. package/dist/slices/designs/calls.js +124 -0
  244. package/dist/slices/designs/check-context.js +38 -0
  245. package/dist/slices/designs/check-text.js +43 -0
  246. package/dist/slices/designs/fields.js +88 -0
  247. package/dist/slices/designs/frames.js +34 -0
  248. package/dist/slices/designs/gate.js +128 -0
  249. package/dist/slices/designs/layout.js +293 -0
  250. package/dist/slices/designs/model.js +35 -0
  251. package/dist/slices/designs/pdf-job.js +23 -0
  252. package/dist/slices/designs/pdf-pages.js +30 -0
  253. package/dist/slices/designs/pdf-runner.js +68 -0
  254. package/dist/slices/designs/pdf-worker.js +24 -0
  255. package/dist/slices/designs/picture-size.js +79 -0
  256. package/dist/slices/designs/pictures.js +55 -0
  257. package/dist/slices/designs/quote.js +24 -0
  258. package/dist/slices/designs/render-args.js +36 -0
  259. package/dist/slices/designs/render-input.js +59 -0
  260. package/dist/slices/designs/render.js +215 -0
  261. package/dist/slices/designs/repo.js +101 -0
  262. package/dist/slices/designs/requests.js +80 -0
  263. package/dist/slices/designs/runner.js +239 -0
  264. package/dist/slices/designs/sample-repo.js +91 -0
  265. package/dist/slices/designs/sample.js +222 -0
  266. package/dist/slices/designs/schema.js +87 -0
  267. package/dist/slices/designs/sections.js +44 -0
  268. package/dist/slices/designs/service.js +233 -0
  269. package/dist/slices/designs/steps-repo.js +102 -0
  270. package/dist/slices/designs/studio-kind.js +39 -0
  271. package/dist/slices/designs/template-checks.js +44 -0
  272. package/dist/slices/designs/template-make.js +118 -0
  273. package/dist/slices/designs/templates.js +218 -0
  274. package/dist/slices/document/fonts.js +2 -1
  275. package/dist/slices/episodes/summarize.js +5 -1
  276. package/dist/slices/estimate/index.js +45 -7
  277. package/dist/slices/fonts/catalog.js +3 -2
  278. package/dist/slices/fonts/discovery.js +3 -2
  279. package/dist/slices/fonts/files.js +2 -6
  280. package/dist/slices/fonts/index.js +0 -1
  281. package/dist/slices/fonts/sfnt.js +45 -10
  282. package/dist/slices/images/appearance.js +6 -15
  283. package/dist/slices/images/scenes.js +100 -27
  284. package/dist/slices/keywords/digest.js +84 -0
  285. package/dist/slices/keywords/facts.js +92 -0
  286. package/dist/slices/keywords/fill.js +80 -0
  287. package/dist/slices/keywords/model.js +75 -0
  288. package/dist/slices/keywords/rules.js +187 -0
  289. package/dist/slices/keywords/schema.js +28 -0
  290. package/dist/slices/keywords/specs.js +260 -0
  291. package/dist/slices/keywords/values.js +74 -0
  292. package/dist/slices/library/lint.js +29 -12
  293. package/dist/slices/library/model.js +2 -0
  294. package/dist/slices/library/photorealistic.js +4 -12
  295. package/dist/slices/library/rename.js +9 -7
  296. package/dist/slices/library/save.js +14 -10
  297. package/dist/slices/library/slots.js +37 -11
  298. package/dist/slices/library/transfer.js +2 -1
  299. package/dist/slices/library/used-by.js +25 -10
  300. package/dist/slices/loudness/loudnorm.js +51 -11
  301. package/dist/slices/model-checks/cost.js +61 -0
  302. package/dist/slices/model-checks/listing.js +124 -0
  303. package/dist/slices/model-checks/model.js +20 -0
  304. package/dist/slices/model-checks/record.js +32 -0
  305. package/dist/slices/model-checks/repo.js +120 -0
  306. package/dist/slices/model-checks/rules.js +112 -0
  307. package/dist/slices/model-checks/run.js +143 -0
  308. package/dist/slices/model-checks/service.js +198 -0
  309. package/dist/slices/model-upkeep/switch.js +70 -25
  310. package/dist/slices/model-upkeep/usage.js +26 -11
  311. package/dist/slices/narration/breaths.js +2 -1
  312. package/dist/slices/narration/clean-pieces.js +2 -1
  313. package/dist/slices/narration/concat.js +2 -1
  314. package/dist/slices/narration/peaks.js +3 -4
  315. package/dist/slices/narration/pronunciation-chunks.js +4 -3
  316. package/dist/slices/narration/pronunciation.js +3 -2
  317. package/dist/slices/narration/steering.js +4 -3
  318. package/dist/slices/notifications/notifier.js +4 -3
  319. package/dist/slices/notifications/rules.js +5 -2
  320. package/dist/slices/notifications/settings.js +4 -12
  321. package/dist/slices/notifications/subject.js +2 -1
  322. package/dist/slices/onboarding/copy.js +2 -1
  323. package/dist/slices/onboarding/first-run.js +3 -2
  324. package/dist/slices/onboarding/full-video.js +3 -2
  325. package/dist/slices/onboarding/install.js +4 -3
  326. package/dist/slices/onboarding/model.js +0 -16
  327. package/dist/slices/onboarding/packs.js +22 -22
  328. package/dist/slices/onboarding/quick-short.js +2 -1
  329. package/dist/slices/onboarding/sample.js +0 -1
  330. package/dist/slices/onboarding/state.js +2 -11
  331. package/dist/slices/onboarding/template.js +2 -1
  332. package/dist/slices/patch-notes/library.js +3 -2
  333. package/dist/slices/play-drafts/convert.js +4 -0
  334. package/dist/slices/play-drafts/review-inputs.js +8 -2
  335. package/dist/slices/play-drafts/review.js +5 -4
  336. package/dist/slices/play-drafts/schema.js +4 -0
  337. package/dist/slices/project-templates/from-project.js +9 -2
  338. package/dist/slices/project-templates/repo.js +6 -1
  339. package/dist/slices/project-templates/setup.js +8 -0
  340. package/dist/slices/project-templates/stand-ins.js +22 -0
  341. package/dist/slices/rebuild/activity.js +2 -0
  342. package/dist/slices/rebuild/admission-repo.js +3 -2
  343. package/dist/slices/rebuild/narration-history.js +4 -5
  344. package/dist/slices/rebuild/narration-retry.js +2 -1
  345. package/dist/slices/rebuild/preview-details.js +27 -9
  346. package/dist/slices/rebuild/preview-plan.js +3 -8
  347. package/dist/slices/rebuild/recipe-build.js +8 -3
  348. package/dist/slices/rebuild/recipe-describe.js +3 -3
  349. package/dist/slices/rebuild/recipe-digest.js +58 -0
  350. package/dist/slices/rebuild/recipe-input-schema.js +10 -0
  351. package/dist/slices/rebuild/recipe-keywords.js +71 -0
  352. package/dist/slices/rebuild/recipe-legacy.js +5 -1
  353. package/dist/slices/rebuild/recipe-model.js +4 -0
  354. package/dist/slices/rebuild/recipe-narration-text.js +2 -1
  355. package/dist/slices/rebuild/recipe-preparation.js +3 -3
  356. package/dist/slices/rebuild/recipe-reference.js +8 -4
  357. package/dist/slices/rebuild/recipe-reviews.js +27 -7
  358. package/dist/slices/rebuild/recipe-save.js +6 -3
  359. package/dist/slices/rebuild/recipe-scenes.js +40 -11
  360. package/dist/slices/rebuild/recipe-shorts.js +8 -8
  361. package/dist/slices/rebuild/recipe-social.js +46 -0
  362. package/dist/slices/rebuild/recipe-text.js +64 -20
  363. package/dist/slices/rebuild/recipe-validation.js +98 -28
  364. package/dist/slices/rebuild/recipe-visual.js +55 -16
  365. package/dist/slices/rebuild/recipe-work.js +15 -3
  366. package/dist/slices/rebuild/recipe-youtube.js +4 -3
  367. package/dist/slices/rebuild/recovery-selection.js +3 -1
  368. package/dist/slices/rebuild/recovery.js +3 -2
  369. package/dist/slices/rebuild/repo.js +4 -3
  370. package/dist/slices/rebuild/review-promise.js +25 -0
  371. package/dist/slices/rebuild/review-redo.js +2 -1
  372. package/dist/slices/rebuild/runtime-admission.js +4 -3
  373. package/dist/slices/rebuild/runtime-agent-review.js +11 -5
  374. package/dist/slices/rebuild/runtime-animate.js +9 -8
  375. package/dist/slices/rebuild/runtime-article.js +3 -2
  376. package/dist/slices/rebuild/runtime-checkpoints.js +5 -4
  377. package/dist/slices/rebuild/runtime-document.js +8 -11
  378. package/dist/slices/rebuild/runtime-export-bed.js +6 -11
  379. package/dist/slices/rebuild/runtime-export-edit.js +6 -15
  380. package/dist/slices/rebuild/runtime-export-inputs.js +7 -7
  381. package/dist/slices/rebuild/runtime-export-short.js +8 -4
  382. package/dist/slices/rebuild/runtime-export.js +34 -12
  383. package/dist/slices/rebuild/runtime-figure-card.js +2 -1
  384. package/dist/slices/rebuild/runtime-image.js +4 -9
  385. package/dist/slices/rebuild/runtime-instructions.js +2 -0
  386. package/dist/slices/rebuild/runtime-keywords.js +59 -0
  387. package/dist/slices/rebuild/runtime-lines.js +2 -11
  388. package/dist/slices/rebuild/runtime-local.js +14 -20
  389. package/dist/slices/rebuild/runtime-narration-publication.js +2 -5
  390. package/dist/slices/rebuild/runtime-narration-reuse.js +9 -13
  391. package/dist/slices/rebuild/runtime-narration-text.js +10 -15
  392. package/dist/slices/rebuild/runtime-pictures.js +2 -4
  393. package/dist/slices/rebuild/runtime-piece-label.js +10 -6
  394. package/dist/slices/rebuild/runtime-plan.js +31 -5
  395. package/dist/slices/rebuild/runtime-provider.js +60 -19
  396. package/dist/slices/rebuild/runtime-publication.js +10 -9
  397. package/dist/slices/rebuild/runtime-review.js +33 -28
  398. package/dist/slices/rebuild/runtime-run.js +4 -1
  399. package/dist/slices/rebuild/runtime-shorts.js +25 -45
  400. package/dist/slices/rebuild/runtime-store.js +3 -8
  401. package/dist/slices/rebuild/runtime-subtitles.js +16 -14
  402. package/dist/slices/rebuild/runtime-voices.js +8 -15
  403. package/dist/slices/rebuild/runtime-youtube.js +11 -29
  404. package/dist/slices/rebuild/transition-repo.js +6 -5
  405. package/dist/slices/rebuild/work-records.js +6 -0
  406. package/dist/slices/reruns/cascade.js +1 -2
  407. package/dist/slices/reruns/index.js +6 -9
  408. package/dist/slices/reviews/external-model.js +3 -0
  409. package/dist/slices/reviews/external-repo.js +2 -1
  410. package/dist/slices/reviews/external-rows.js +2 -3
  411. package/dist/slices/reviews/model.js +26 -8
  412. package/dist/slices/reviews/repo.js +2 -1
  413. package/dist/slices/reviews/rules.js +1 -0
  414. package/dist/slices/reviews/view.js +5 -2
  415. package/dist/slices/revisions/downloads.js +4 -5
  416. package/dist/slices/revisions/mutation-assets.js +7 -4
  417. package/dist/slices/revisions/mutation-cues.js +2 -5
  418. package/dist/slices/revisions/mutations.js +0 -1
  419. package/dist/slices/revisions/repo.js +3 -1
  420. package/dist/slices/revisions/schema.js +1 -0
  421. package/dist/slices/revisions/view.js +20 -0
  422. package/dist/slices/run-cost/limits.js +5 -2
  423. package/dist/slices/run-cost/meter.js +12 -5
  424. package/dist/slices/run-cost/panel.js +8 -74
  425. package/dist/slices/run-cost/plan-readings.js +127 -0
  426. package/dist/slices/run-cost/plan-use.js +169 -0
  427. package/dist/slices/run-cost/pricing.js +3 -3
  428. package/dist/slices/run-cost/week.js +32 -4
  429. package/dist/slices/schedules/agenda.js +11 -2
  430. package/dist/slices/schedules/by-date.js +42 -0
  431. package/dist/slices/schedules/calendar.js +5 -0
  432. package/dist/slices/schedules/design-agenda.js +36 -0
  433. package/dist/slices/schedules/design-check.js +41 -0
  434. package/dist/slices/schedules/design-run.js +126 -0
  435. package/dist/slices/schedules/next-run.js +62 -0
  436. package/dist/slices/schedules/prepare.js +18 -2
  437. package/dist/slices/schedules/repo.js +24 -16
  438. package/dist/slices/schedules/rescore.js +35 -0
  439. package/dist/slices/schedules/scheduler.js +13 -3
  440. package/dist/slices/schedules/schema.js +54 -3
  441. package/dist/slices/schedules/score.js +107 -0
  442. package/dist/slices/schedules/service.js +57 -24
  443. package/dist/slices/schedules/topic-list.js +15 -0
  444. package/dist/slices/schedules/topic-scores.js +104 -0
  445. package/dist/slices/schedules/topics.js +48 -36
  446. package/dist/slices/settings/cli-paths.js +4 -6
  447. package/dist/slices/settings/first-run.js +2 -11
  448. package/dist/slices/settings/model-reach.js +3 -17
  449. package/dist/slices/settings/model.js +10 -0
  450. package/dist/slices/settings/playback.js +33 -22
  451. package/dist/slices/settings/probe-read.js +16 -0
  452. package/dist/slices/settings/repo.js +33 -0
  453. package/dist/slices/settings/tutorial.js +2 -2
  454. package/dist/slices/settings/voices.js +4 -12
  455. package/dist/slices/shorts/pick.js +2 -15
  456. package/dist/slices/shorts/prompts.js +2 -13
  457. package/dist/slices/shorts/render.js +3 -0
  458. package/dist/slices/social/channel.js +44 -0
  459. package/dist/slices/social/defaults.js +17 -0
  460. package/dist/slices/social/due.js +36 -0
  461. package/dist/slices/social/extension.js +18 -0
  462. package/dist/slices/social/fit.js +115 -0
  463. package/dist/slices/social/limits.js +186 -0
  464. package/dist/slices/social/links.js +21 -0
  465. package/dist/slices/social/media.js +131 -0
  466. package/dist/slices/social/model.js +75 -0
  467. package/dist/slices/social/notice.js +19 -0
  468. package/dist/slices/social/pack.js +176 -0
  469. package/dist/slices/social/post-prompt.js +55 -0
  470. package/dist/slices/social/queue.js +31 -0
  471. package/dist/slices/social/repo.js +133 -0
  472. package/dist/slices/social/reshape.js +97 -0
  473. package/dist/slices/social/rules.js +5 -0
  474. package/dist/slices/social/schema.js +70 -0
  475. package/dist/slices/social/serve.js +51 -0
  476. package/dist/slices/social/service.js +128 -0
  477. package/dist/slices/social/settings.js +21 -0
  478. package/dist/slices/social/sources.js +114 -0
  479. package/dist/slices/social/state.js +110 -0
  480. package/dist/slices/social/text.js +149 -0
  481. package/dist/slices/storage/arrange.js +3 -1
  482. package/dist/slices/storage/assets.js +8 -10
  483. package/dist/slices/storage/backup-designs.js +74 -0
  484. package/dist/slices/storage/backup-export.js +8 -3
  485. package/dist/slices/storage/backup-format.js +10 -0
  486. package/dist/slices/storage/backup-import.js +20 -2
  487. package/dist/slices/storage/delete-project.js +6 -5
  488. package/dist/slices/storage/files-location.js +6 -8
  489. package/dist/slices/storage/layout.js +13 -2
  490. package/dist/slices/storage/model.js +3 -0
  491. package/dist/slices/storage/portable.js +16 -11
  492. package/dist/slices/storage/schema.js +2 -0
  493. package/dist/slices/storage/staging-refs.js +2 -1
  494. package/dist/slices/storage/staging.js +6 -9
  495. package/dist/slices/storage/trim.js +10 -3
  496. package/dist/slices/studio/calendar.js +4 -1
  497. package/dist/slices/studio/pick.js +2 -11
  498. package/dist/slices/studio/plan.js +6 -15
  499. package/dist/slices/studio/queue.js +3 -14
  500. package/dist/slices/studio/releases.js +26 -0
  501. package/dist/slices/studio/settings.js +14 -51
  502. package/dist/slices/studio/videos.js +12 -0
  503. package/dist/slices/style-preview/render.js +34 -13
  504. package/dist/slices/style-preview/schema.js +13 -0
  505. package/dist/slices/style-preview/service.js +2 -1
  506. package/dist/slices/style-preview/settings.js +33 -5
  507. package/dist/slices/telemetry/collector-client.js +2 -4
  508. package/dist/slices/telemetry/flush.js +2 -4
  509. package/dist/slices/telemetry/model.js +0 -7
  510. package/dist/slices/telemetry/record.js +10 -3
  511. package/dist/slices/template-studio/anatomy-view.js +118 -0
  512. package/dist/slices/template-studio/anatomy.js +73 -0
  513. package/dist/slices/template-studio/bakeoff-make.js +225 -0
  514. package/dist/slices/template-studio/bakeoff-repo.js +83 -0
  515. package/dist/slices/template-studio/bakeoff.js +333 -0
  516. package/dist/slices/template-studio/budget.js +103 -0
  517. package/dist/slices/template-studio/checks.js +273 -0
  518. package/dist/slices/template-studio/document-sample.js +56 -0
  519. package/dist/slices/template-studio/edit.js +15 -0
  520. package/dist/slices/template-studio/for-all.js +161 -0
  521. package/dist/slices/template-studio/free-video.js +31 -0
  522. package/dist/slices/template-studio/free.js +176 -0
  523. package/dist/slices/template-studio/gate.js +40 -0
  524. package/dist/slices/template-studio/hidden.js +20 -0
  525. package/dist/slices/template-studio/jobs.js +79 -0
  526. package/dist/slices/template-studio/loudness.js +111 -0
  527. package/dist/slices/template-studio/make.js +40 -0
  528. package/dist/slices/template-studio/model.js +54 -0
  529. package/dist/slices/template-studio/pack-import.js +253 -0
  530. package/dist/slices/template-studio/pack-samples.js +60 -0
  531. package/dist/slices/template-studio/pack.js +205 -0
  532. package/dist/slices/template-studio/paid-view.js +66 -0
  533. package/dist/slices/template-studio/paid.js +348 -0
  534. package/dist/slices/template-studio/quote.js +148 -0
  535. package/dist/slices/template-studio/ready.js +104 -0
  536. package/dist/slices/template-studio/repo.js +134 -0
  537. package/dist/slices/template-studio/reviews-path.js +58 -0
  538. package/dist/slices/template-studio/runner.js +84 -0
  539. package/dist/slices/template-studio/save.js +236 -0
  540. package/dist/slices/template-studio/say-it-as.js +248 -0
  541. package/dist/slices/template-studio/schedule-gate.js +34 -0
  542. package/dist/slices/template-studio/schedule-price.js +30 -0
  543. package/dist/slices/template-studio/schedule-sample.js +87 -0
  544. package/dist/slices/template-studio/schema.js +218 -0
  545. package/dist/slices/template-studio/scope.js +124 -0
  546. package/dist/slices/template-studio/sections.js +176 -0
  547. package/dist/slices/template-studio/service.js +173 -0
  548. package/dist/slices/template-studio/status.js +174 -0
  549. package/dist/slices/template-studio/topic.js +223 -0
  550. package/dist/slices/trash/designs.js +76 -0
  551. package/dist/slices/trash/model.js +13 -4
  552. package/dist/slices/trash/service.js +52 -2
  553. package/dist/slices/tutorials/library.js +3 -2
  554. package/dist/slices/video/ass-text.js +17 -0
  555. package/dist/slices/video/audio-export.js +2 -64
  556. package/dist/slices/video/encoding.js +160 -0
  557. package/dist/slices/video/ffmpeg.js +23 -21
  558. package/dist/slices/video/figure-card.js +1 -15
  559. package/dist/slices/video/slideshow.js +43 -18
  560. package/dist/slices/voices/palette.js +0 -3
  561. package/dist/slices/youtube/answer.js +2 -15
  562. package/dist/slices/youtube/edits-repo.js +2 -11
  563. package/dist/tutorials/Agents-MCP.md +2 -1
  564. package/dist/tutorials/Assistant.md +42 -0
  565. package/dist/tutorials/Channels.md +2 -0
  566. package/dist/tutorials/Home-and-Projects.md +19 -0
  567. package/dist/tutorials/Install.md +1 -0
  568. package/dist/tutorials/Keywords-Slopify-Fills.md +87 -0
  569. package/dist/tutorials/Models.md +33 -1
  570. package/dist/tutorials/Notifications.md +6 -0
  571. package/dist/tutorials/Play-Images.md +4 -0
  572. package/dist/tutorials/Posting-to-Social-Sites.md +121 -0
  573. package/dist/tutorials/Prompts.md +10 -6
  574. package/dist/tutorials/Remote-Access.md +63 -0
  575. package/dist/tutorials/Reviews-and-Checkpoints.md +13 -2
  576. package/dist/tutorials/Schedules.md +21 -1
  577. package/dist/tutorials/Settings-Reference.md +50 -8
  578. package/dist/tutorials/Template-Studio.md +125 -0
  579. package/dist/tutorials/Templates.md +3 -0
  580. package/dist/tutorials/_Sidebar.md +5 -0
  581. package/dist/updater/work-in-progress.js +42 -12
  582. package/dist/web/assets/index-CkAI_2E7.css +1 -0
  583. package/dist/web/assets/index-DcD0cdfo.js +334 -0
  584. package/dist/web/assets/{pdf-T7MCwjIv.js → pdf-TODRwF31.js} +1 -1
  585. package/dist/web/index.html +2 -2
  586. package/package.json +5 -1
  587. package/dist/slices/article/provided-entries.js +0 -66
  588. package/dist/slices/article/run.js +0 -145
  589. package/dist/slices/images/run.js +0 -234
  590. package/dist/slices/narration/legacy-plan.js +0 -58
  591. package/dist/slices/narration/run.js +0 -270
  592. package/dist/slices/rebuild/runtime-actions.js +0 -90
  593. package/dist/slices/research/run.js +0 -224
  594. package/dist/slices/subtitles/prepare.js +0 -194
  595. package/dist/slices/subtitles/transcript.js +0 -34
  596. package/dist/slices/thumbnail/run.js +0 -219
  597. package/dist/slices/video/audio-inputs.js +0 -33
  598. package/dist/slices/video/reuse-audio.js +0 -62
  599. package/dist/slices/video/run.js +0 -78
  600. package/dist/slices/video/write-export.js +0 -158
  601. package/dist/slices/video/write-subtitles.js +0 -59
  602. package/dist/web/assets/index-BadsoORk.css +0 -1
  603. package/dist/web/assets/index-D8LqNsCl.js +0 -326
@@ -1,4 +1,5 @@
1
1
  import { z } from "zod";
2
+ import { channelSocialOf } from "../social/channel.js";
2
3
  import { aiDisclosureSettings } from "../studio/disclosure.js";
3
4
  import { channelAmbientBedSchema } from "../video/ambient-bed-schema.js";
4
5
  import { castKinds, defaultChannelId, } from "./model.js";
@@ -25,6 +26,8 @@ const channelRow = z.object({
25
26
  brand_json: z.string(),
26
27
  series_brief: z.string(),
27
28
  ai_disclosure: z.enum(aiDisclosureSettings),
29
+ x_tier: z.string().optional(),
30
+ social_accounts: z.string().optional(),
28
31
  version: z.number(),
29
32
  created_at: z.string(),
30
33
  updated_at: z.string(),
@@ -69,6 +72,7 @@ function channelOf(row) {
69
72
  brand: brandSchema.parse(JSON.parse(value.brand_json)),
70
73
  seriesBrief: value.series_brief,
71
74
  aiDisclosure: value.ai_disclosure,
75
+ ...((social) => ({ xTier: social.xTier, socialAccounts: social.accounts }))(channelSocialOf(value.x_tier, value.social_accounts)),
72
76
  version: value.version,
73
77
  createdAt: value.created_at,
74
78
  updatedAt: value.updated_at,
@@ -1,5 +1,6 @@
1
1
  import { z } from "zod";
2
2
  import { languageSchema } from "../../kernel/ports/languages.js";
3
+ import { socialPlatforms, xTiers } from "../social/model.js";
3
4
  import { aiDisclosureSettings } from "../studio/disclosure.js";
4
5
  import { ambientBedProblems } from "../video/ambient-bed.js";
5
6
  import { channelAmbientBedSchema } from "../video/ambient-bed-schema.js";
@@ -75,6 +76,11 @@ export const channelUpdateSchema = z
75
76
  name,
76
77
  brand: brandKitSchema,
77
78
  seriesBrief: z.string().max(10000, "Keep the series brief to 10,000 characters or fewer."),
79
+ // Brand → Posting (`slices/social`): left out, they stay as they are.
80
+ xTier: z.enum(xTiers, "Choose Free, Premium or Premium+.").optional(),
81
+ socialAccounts: z
82
+ .partialRecord(z.enum(socialPlatforms), z.string().max(260, "Keep each account name to 260 characters or fewer."))
83
+ .optional(),
78
84
  baseVersion: z.number().int().positive(),
79
85
  })
80
86
  .strict()
@@ -1,4 +1,5 @@
1
1
  import { transact } from "../../kernel/db/tx.js";
2
+ import { accountsProblem, writeChannelSocial } from "../social/channel.js";
2
3
  import { defaultChannelId } from "./model.js";
3
4
  import { castMemberById, castOfChannel, channelById, channelSummaries } from "./repo.js";
4
5
  import { castMemberCreateSchema, castMemberUpdateSchema, castMoveSchema, channelAiDisclosureSchema, channelCreateSchema, channelUpdateSchema, templateChannelSchema, } from "./schema.js";
@@ -50,9 +51,18 @@ export function updateChannel(deps, id, input) {
50
51
  if (previous.version !== parsed.data.baseVersion)
51
52
  return { ok: false, reason: "conflict" };
52
53
  const brand = parsed.data.brand;
54
+ const accountsWrong = parsed.data.socialAccounts === undefined
55
+ ? undefined
56
+ : accountsProblem(parsed.data.socialAccounts);
57
+ if (accountsWrong !== undefined)
58
+ return { ok: false, reason: "invalid-input", message: accountsWrong };
53
59
  deps.db
54
60
  .prepare("UPDATE channels SET name=?,brand_json=?,series_brief=?,version=version+1,updated_at=? WHERE id=? AND version=?")
55
61
  .run(parsed.data.name, JSON.stringify(brand), parsed.data.seriesBrief, deps.clock.now().toISOString(), id, parsed.data.baseVersion);
62
+ writeChannelSocial(deps.db, id, {
63
+ xTier: parsed.data.xTier,
64
+ accounts: parsed.data.socialAccounts,
65
+ });
56
66
  const saved = channelById(deps.db, id);
57
67
  if (saved === undefined)
58
68
  throw new Error("The saved channel could not be read back");
@@ -1,6 +1,6 @@
1
1
  import { z } from "zod";
2
2
  import { transact } from "../../kernel/db/tx.js";
3
- import { readSetting, writeSetting } from "../settings/repo.js";
3
+ import { readJsonSetting, writeSetting } from "../settings/repo.js";
4
4
  import { channelById } from "./repo.js";
5
5
  import { titlesFromCsv, titlesFromLines } from "./studio-csv.js";
6
6
  export const videoTitleMax = 500;
@@ -47,7 +47,7 @@ export function previewChannelVideos(deps, channelId, input) {
47
47
  const read = importOf(input);
48
48
  if (!read.ok)
49
49
  return read;
50
- const filter = storedFilter(readSetting(deps.db, filterKey(channelId)));
50
+ const filter = readJsonSetting(deps.db, filterKey(channelId), z.string(), "");
51
51
  return { ok: true, value: { titles: read.value.titles, filter } };
52
52
  }
53
53
  // Titles already on the channel (any case) and repeats within the import are skipped.
@@ -69,18 +69,6 @@ export function importChannelVideos(deps, channelId, input) {
69
69
  return { ok: true, value: { added, skipped: titles.length - added } };
70
70
  });
71
71
  }
72
- // Stored as JSON, like every row of the `settings` table a backup carries.
73
- function storedFilter(stored) {
74
- if (stored === undefined)
75
- return "";
76
- try {
77
- const value = JSON.parse(stored);
78
- return typeof value === "string" ? value : "";
79
- }
80
- catch {
81
- return "";
82
- }
83
- }
84
72
  function importOf(input) {
85
73
  const parsed = importSchema.safeParse(input);
86
74
  if (!parsed.success)
@@ -1,2 +1 @@
1
1
  export { approveCheckpoint, checkpointForWork, listCheckpoints, saveCheckpointSet, settleCheckpointClosures, } from "./repo.js";
2
- export { checkpointApprovalSchema, checkpointRowSchema, checkpointSetSchema, checkpointStageSchema, checkpointStateSchema, } from "./schema.js";
@@ -1,31 +1,5 @@
1
- import { z } from "zod";
2
1
  import { checkRuntimeModel } from "../../catalog/runtime-models.js";
3
- import { thinkingModes } from "../../kernel/ports/llm.js";
4
2
  import { readinessIsUsable, } from "../../kernel/ports/model.js";
5
- import { chunkModes } from "../narration/chunk.js";
6
- const choice = z
7
- .object({
8
- provider: z.string().trim().min(1).max(200),
9
- model: z.string().trim().min(1).max(200),
10
- thinking: z.enum(thinkingModes).optional(),
11
- })
12
- .strict();
13
- export const providerChangesSchema = z
14
- .object({
15
- llm: choice.optional(),
16
- audio: choice.extend({ voice: z.string().trim().min(1).max(200) }).optional(),
17
- images: choice.optional(),
18
- chunking: z
19
- .object({
20
- mode: z.enum(chunkModes),
21
- words: z.number().int().min(1).max(10000).optional(),
22
- characters: z.number().int().min(1).max(1000000).optional(),
23
- })
24
- .strict()
25
- .optional(),
26
- })
27
- .strict()
28
- .refine((value) => Object.keys(value).length > 0, "Change at least one provider before saving.");
29
3
  export function validateLocalProviderChanges(changes, current) {
30
4
  const fields = [];
31
5
  const families = { llm: "llm", audio: "tts", images: "image" };
@@ -0,0 +1,66 @@
1
+ import { castMemberById, imageBlob } from "../channels/repo.js";
2
+ import { designFields, fieldProblems, fillDocument, missingSentence, missingValues, } from "./fields.js";
3
+ import { artworkAspect } from "./frames.js";
4
+ import { designDocumentSchema } from "./schema.js";
5
+ export function artworkRequest(document, values) {
6
+ const parsed = designDocumentSchema.safeParse(document);
7
+ if (!parsed.success)
8
+ return {
9
+ ok: false,
10
+ reason: "invalid",
11
+ message: "This design has no settings to make artwork from. Choose its picture, text and formats first.",
12
+ };
13
+ const doc = parsed.data;
14
+ if (doc.picture.source !== "generate")
15
+ return {
16
+ ok: false,
17
+ reason: "invalid",
18
+ message: "This design uses its own picture as the artwork, so there is nothing to make. Edit the picture instead, or switch Picture to Made by the AI.",
19
+ };
20
+ const problem = fieldProblems(doc)[0];
21
+ if (problem !== undefined)
22
+ return { ok: false, reason: "invalid", message: problem.sentence };
23
+ const missing = missingValues(doc, values).filter((name) => detectedInPicture(doc, name));
24
+ if (missing.length > 0)
25
+ return { ok: false, reason: "missing-values", message: missingSentence(missing) };
26
+ const filled = fillDocument(doc, values);
27
+ const words = filled.text.blocks.map((block) => block.text.trim()).filter((text) => text !== "");
28
+ const prompt = doc.picture.aiText && words.length > 0
29
+ ? `${filled.picture.prompt}\n\nDraw this text into the picture as part of the design, spelled exactly as written, each line on its own:\n${words.map((line) => `"${line}"`).join("\n")}`
30
+ : `${filled.picture.prompt}\n\nLeave room for a title and details to be set over the picture; draw no text, letters or logos.`;
31
+ return {
32
+ ok: true,
33
+ value: {
34
+ prompt,
35
+ aspect: artworkAspect(doc.formats.map((one) => one.format)),
36
+ castMemberIds: doc.picture.castMemberIds,
37
+ provider: doc.picture.provider,
38
+ model: doc.picture.model,
39
+ },
40
+ };
41
+ }
42
+ // The text fields only matter to the artwork when the AI draws the text.
43
+ function detectedInPicture(doc, name) {
44
+ if (doc.picture.aiText)
45
+ return designFields({ ...doc, brochure: undefined }).includes(name);
46
+ return designFields({ ...doc, text: { ...doc.text, blocks: [] }, brochure: undefined }).includes(name);
47
+ }
48
+ // Each chosen cast member with the pictures they have, in the order chosen.
49
+ export function castReferences(db, ids) {
50
+ const references = [];
51
+ for (const id of ids) {
52
+ const member = castMemberById(db, id);
53
+ if (member === undefined)
54
+ return {
55
+ ok: false,
56
+ reason: "invalid",
57
+ message: "A cast member you chose was deleted. Choose the cast again, then press the button again.",
58
+ };
59
+ const images = member.images.flatMap((image) => {
60
+ const blob = image.state === "ready" && image.sha256 !== null ? imageBlob(db, image.sha256) : undefined;
61
+ return blob === undefined ? [] : [blob];
62
+ });
63
+ references.push({ name: member.name, description: member.description, images });
64
+ }
65
+ return { ok: true, value: references };
66
+ }
@@ -0,0 +1,23 @@
1
+ import { z } from "zod";
2
+ import { readJsonSetting } from "../settings/repo.js";
3
+ import { sampleSpentToday } from "./sample-repo.js";
4
+ // What a design template's samples may spend per day, in dollars, before the editor holds a
5
+ // sample for a person to start.
6
+ // No row is seeded: an absent row reads as the default, and only a changed budget travels in
7
+ // backups.
8
+ export const dailySampleBudgetKey = "designs.dailySampleBudget";
9
+ export const dailySampleBudgetSchema = z.number().min(0).max(1000);
10
+ export const defaultDailySampleBudget = 1;
11
+ export function dailySampleBudget(db, log) {
12
+ return readJsonSetting(db, dailySampleBudgetKey, dailySampleBudgetSchema, defaultDailySampleBudget, log);
13
+ }
14
+ export function budgetSentence(total, budget) {
15
+ return `Waiting: this sample would take today's sample spending to $${total.toFixed(2)}, over the $${budget.toFixed(2)} a day samples may spend. Press Make sample to make it anyway; the limit starts again at midnight.`;
16
+ }
17
+ // Settings → Template studio shows it beside the budget: what design samples spent since local
18
+ // midnight and what is left of today's budget.
19
+ export function designBudgetRoom(db, now, timeZone) {
20
+ const budget = dailySampleBudget(db);
21
+ const spent = sampleSpentToday(db, now, timeZone);
22
+ return { budget, spent, left: Math.max(0, budget - spent) };
23
+ }
@@ -0,0 +1,124 @@
1
+ import { transact } from "../../kernel/db/tx.js";
2
+ import { errorText } from "../../kernel/errors.js";
3
+ import { isProviderError } from "../../kernel/ports/model.js";
4
+ import { castImageMaxBytes, storeImageBlob } from "../channels/images.js";
5
+ import { imageBlob } from "../channels/repo.js";
6
+ import { backupMaxImageBytes } from "../storage/backup-format.js";
7
+ import { castReferences } from "./artwork.js";
8
+ import { pictureSize } from "./picture-size.js";
9
+ import { updateDesign } from "./repo.js";
10
+ import { settleStep, stepById } from "./steps-repo.js";
11
+ // The background half of the runner: each "making" step's provider call, at most two at once,
12
+ // settled into its step (and made the design's current version when it lands).
13
+ export const maxConcurrentDesignCalls = 2;
14
+ export const noProviderMessage = "No image provider is set up. Add one in Settings → Providers, then press the button again.";
15
+ export function createDesignCalls(deps) {
16
+ let running = 0;
17
+ const waiting = [];
18
+ const inFlight = new Set();
19
+ const slot = async () => {
20
+ if (running < maxConcurrentDesignCalls) {
21
+ running += 1;
22
+ return;
23
+ }
24
+ await new Promise((resolve) => waiting.push(resolve));
25
+ };
26
+ const release = () => {
27
+ const next = waiting.shift();
28
+ if (next === undefined)
29
+ running -= 1;
30
+ else
31
+ next();
32
+ };
33
+ const finish = (designId, stepId, outcome) => {
34
+ const at = deps.clock.now().toISOString();
35
+ transact(deps.db, () => {
36
+ if (settleStep(deps.db, stepId, outcome, at) && outcome.state === "ready")
37
+ updateDesign(deps.db, designId, undefined, { headStepId: stepId }, at);
38
+ });
39
+ try {
40
+ deps.emit?.({ type: "design.changed", designId });
41
+ }
42
+ catch (error) {
43
+ deps.log.write("warn", "designs.emit", { detail: errorText(error) });
44
+ }
45
+ };
46
+ const call = async (designId, stepId) => {
47
+ await slot();
48
+ try {
49
+ const step = stepById(deps.db, stepId);
50
+ const generate = deps.generate;
51
+ if (step === undefined ||
52
+ step.state !== "making" ||
53
+ step.provider === null ||
54
+ step.model === null)
55
+ return;
56
+ if (generate === undefined)
57
+ throw new Error(noProviderMessage);
58
+ const cast = castReferences(deps.db, step.inputs.castMemberIds);
59
+ if (!cast.ok)
60
+ throw new Error(cast.message);
61
+ const parent = step.parentId === null ? undefined : stepById(deps.db, step.parentId);
62
+ const source = parent === undefined || parent.sha256 === null
63
+ ? undefined
64
+ : imageBlob(deps.db, parent.sha256);
65
+ if (step.kind === "edit" && source === undefined)
66
+ throw new Error("The version this edit starts from is no longer kept. Continue from another version, then press Edit.");
67
+ const sent = source === undefined || source.bytes.byteLength <= castImageMaxBytes
68
+ ? source
69
+ : await shrunk(source.bytes);
70
+ const image = await generate({
71
+ designId,
72
+ purpose: step.kind === "edit" ? "picture-edit" : "design-art",
73
+ provider: step.provider,
74
+ model: step.model,
75
+ prompt: step.inputs.prompt ?? step.instruction ?? "",
76
+ aspect: step.inputs.aspect ?? "1:1",
77
+ ...(sent === undefined || step.kind !== "edit" ? {} : { edit: { source: sent } }),
78
+ ...(cast.value.length === 0 ? {} : { cast: cast.value }),
79
+ });
80
+ const size = pictureSize(image.bytes);
81
+ if (size === undefined || image.bytes.byteLength > backupMaxImageBytes)
82
+ throw new Error("The provider answered with something that is not a usable PNG or JPEG picture.");
83
+ const sha256 = storeImageBlob(deps.db, image.bytes, image.mime, deps.clock.now().toISOString());
84
+ finish(designId, stepId, { state: "ready", sha256, ...size });
85
+ }
86
+ catch (error) {
87
+ const reason = errorText(error).slice(0, 500);
88
+ deps.log.write("warn", "designs.picture.failed", {
89
+ detail: `${designId}/${stepId}: ${reason}`,
90
+ });
91
+ finish(designId, stepId, { state: "failed", error: failedSentence(error, reason) });
92
+ }
93
+ finally {
94
+ release();
95
+ }
96
+ };
97
+ const shrunk = async (bytes) => {
98
+ if (deps.shrink === undefined)
99
+ throw new Error("The picture is too large to send to the provider.");
100
+ return { bytes: await deps.shrink(bytes), mime: "image/jpeg" };
101
+ };
102
+ const start = (designId, stepId) => {
103
+ const job = call(designId, stepId);
104
+ inFlight.add(job);
105
+ void job.finally(() => inFlight.delete(job));
106
+ };
107
+ return {
108
+ start,
109
+ idle: async () => {
110
+ while (inFlight.size > 0)
111
+ await Promise.allSettled([...inFlight]);
112
+ },
113
+ };
114
+ }
115
+ // Try again is offered only when asking again can help: a model that can't do this, or a key
116
+ // the provider refuses, fails the same way every time, so the sentence says the fix instead.
117
+ function failedSentence(error, reason) {
118
+ const kind = isProviderError(error) ? error.fault.kind : undefined;
119
+ if (kind === "unsupported")
120
+ return `The picture couldn't be made: ${reason}${reason.includes("Made with") ? "" : " Choose another image model under Made with … · change."}`;
121
+ if (kind === "auth" || kind === "missing_key")
122
+ return `The picture couldn't be made: ${reason}${/Settings/.test(reason) ? "" : " Check the provider's key in Settings → Providers, then press Try again."}`;
123
+ return `The picture couldn't be made: ${reason} Press Try again, or choose another image model under Made with … · change.`;
124
+ }
@@ -0,0 +1,38 @@
1
+ import { errorText } from "../../kernel/errors.js";
2
+ import { readFontFile } from "../fonts/files.js";
3
+ // The fonts and providers Mark ready looks at, read from this computer. `main.ts` passes the
4
+ // real font catalogue and provider statuses; tests pass their own.
5
+ export function designCheckContext(deps) {
6
+ return {
7
+ font: async (fontId) => {
8
+ try {
9
+ const font = await deps.resolveFont(fontId);
10
+ const bytes = await readFontFile(font.path);
11
+ return bytes === undefined
12
+ ? undefined
13
+ : { name: font.name, bytes: new Uint8Array(bytes), faceIndex: font.faceIndex };
14
+ }
15
+ catch {
16
+ return undefined;
17
+ }
18
+ },
19
+ providerProblem: async (provider) => {
20
+ let statuses;
21
+ try {
22
+ statuses = await deps.providers();
23
+ }
24
+ catch (error) {
25
+ return `Slopify couldn't check the image provider: ${errorText(error)} Open Settings → Providers, then press Mark ready again.`;
26
+ }
27
+ const status = statuses.find((one) => one.id === provider);
28
+ if (status === undefined)
29
+ return `The image provider “${provider}” isn't one this Slopify knows. Choose another image model under Picture.`;
30
+ const readiness = status.readiness;
31
+ if (readiness.kind === "keyed" && !readiness.hasKey)
32
+ return `${status.displayName} has no API key, so the picture can't be made. Add the key in Settings → Providers, then press Mark ready again.`;
33
+ if (readiness.kind === "cli" && (!readiness.installed || readiness.issue !== undefined))
34
+ return `${status.displayName} can't be used yet: ${readiness.issue ?? "it isn't installed on this computer."} Fix it in Settings → Providers, then press Mark ready again.`;
35
+ return null;
36
+ },
37
+ };
38
+ }
@@ -0,0 +1,43 @@
1
+ import { render } from "../admission/substitute.js";
2
+ import { mappedCodepoints } from "../fonts/sfnt.js";
3
+ const squash = (value) => value.replace(/\s+/g, "");
4
+ export function checkText(input) {
5
+ const problems = [];
6
+ for (const [index, block] of input.text.blocks.entries()) {
7
+ const filled = render(block.text, input.values);
8
+ if (filled.trim() === "")
9
+ continue;
10
+ const where = `text block ${String(index + 1)}`;
11
+ const font = input.fonts[block.fontId];
12
+ if (font === undefined) {
13
+ problems.push({
14
+ blockId: block.id,
15
+ sentence: `The font chosen for ${where} is no longer on this computer. Choose another font for it under Text.`,
16
+ });
17
+ continue;
18
+ }
19
+ const letters = [...new Set([...filled].filter((ch) => ch.trim() !== ""))];
20
+ const found = mappedCodepoints(font.bytes, font.faceIndex, letters.map((ch) => ch.codePointAt(0) ?? 0));
21
+ const missing = found === undefined ? [] : letters.filter((ch) => !found.has(ch.codePointAt(0) ?? 0));
22
+ if (missing.length > 0)
23
+ problems.push({
24
+ blockId: block.id,
25
+ sentence: `The font ${font.name} has no ${missing.map((ch) => `“${ch}”`).join(" ")} for ${where}. Choose another font for it under Text, or upload one that has ${missing.length === 1 ? "it" : "them"}.`,
26
+ });
27
+ const drawn = input.layout.lines
28
+ .filter((line) => line.blockId === block.id)
29
+ .map((line) => line.text)
30
+ .join("");
31
+ if (squash(drawn) !== squash(filled))
32
+ problems.push({
33
+ blockId: block.id,
34
+ sentence: `Slopify couldn't place ${where} exactly as typed on ${input.formatLabel}. Shorten it under Text, then press Make again.`,
35
+ });
36
+ }
37
+ if (input.layout.overflow)
38
+ problems.push({
39
+ blockId: "",
40
+ sentence: `The text doesn't fit on ${input.formatLabel}, even at the smallest readable size. Shorten it or choose a placement with more room under Text.`,
41
+ });
42
+ return problems;
43
+ }
@@ -0,0 +1,88 @@
1
+ import { detectSlots, filledKeywords, render } from "../admission/substitute.js";
2
+ import { isReserved, reservedPrefix } from "../keywords/model.js";
3
+ function bodies(doc) {
4
+ return [
5
+ { where: "the picture prompt", text: doc.picture.prompt },
6
+ ...doc.text.blocks.map((block, index) => ({
7
+ where: `text block ${String(index + 1)}`,
8
+ text: block.text,
9
+ })),
10
+ ...(doc.brochure === undefined
11
+ ? []
12
+ : [
13
+ { where: "the brochure title", text: doc.brochure.title },
14
+ { where: "the brochure text", text: doc.brochure.body },
15
+ ]),
16
+ ];
17
+ }
18
+ export function designFields(doc) {
19
+ const names = [];
20
+ for (const body of bodies(doc))
21
+ for (const name of detectSlots(body.text).names)
22
+ if (!names.includes(name))
23
+ names.push(name);
24
+ return names;
25
+ }
26
+ const slotSentences = {
27
+ unclosed: "has a {{ without its }}",
28
+ empty: "has an empty {{}}",
29
+ nested: "has a { or } inside a {{field}}",
30
+ };
31
+ export function fieldProblems(doc) {
32
+ const problems = [];
33
+ for (const body of bodies(doc))
34
+ for (const error of detectSlots(body.text).errors)
35
+ problems.push({
36
+ kind: "slot",
37
+ where: body.where,
38
+ error: error.kind,
39
+ sentence: `The ${body.where.replace(/^the /, "")} ${slotSentences[error.kind]}. Fix it so every field reads {{Name}}.`,
40
+ });
41
+ const names = designFields(doc);
42
+ for (const name of names)
43
+ if (isReserved(name) || filledKeywords.includes(name))
44
+ problems.push({
45
+ kind: "reserved",
46
+ name,
47
+ sentence: `{{${name}}} is a name Slopify keeps for videos, so a design can't use it. Rename it (for example {{${isReserved(name) ? name.slice(reservedPrefix.length) : name}Text}}).`,
48
+ });
49
+ const dateField = doc.schedule?.dateField;
50
+ if (dateField !== undefined && !names.includes(dateField))
51
+ problems.push({
52
+ kind: "date-field",
53
+ name: dateField,
54
+ sentence: `The schedule reads each row's date from {{${dateField}}}, but no text uses it. Put {{${dateField}}} in a text block, or choose another date field under Schedule.`,
55
+ });
56
+ return problems;
57
+ }
58
+ // A field counts as filled only with something other than spaces.
59
+ export function missingValues(doc, values) {
60
+ return designFields(doc).filter((name) => (Object.hasOwn(values, name) ? (values[name] ?? "") : "").trim() === "");
61
+ }
62
+ export function missingSentence(missing) {
63
+ const list = missing.map((name) => `{{${name}}}`);
64
+ const named = list.length === 1
65
+ ? `${list[0] ?? ""} has`
66
+ : `${list.slice(0, -1).join(", ")} and ${list.at(-1) ?? ""} have`;
67
+ return `${named} no value. Fill ${list.length === 1 ? "it" : "them"} in under Fields, then press Make.`;
68
+ }
69
+ // Every text with its fields filled. Values are inserted verbatim, never read as fields.
70
+ export function fillDocument(doc, values) {
71
+ return {
72
+ ...doc,
73
+ picture: { ...doc.picture, prompt: render(doc.picture.prompt, values) },
74
+ text: {
75
+ ...doc.text,
76
+ blocks: doc.text.blocks.map((block) => ({ ...block, text: render(block.text, values) })),
77
+ },
78
+ ...(doc.brochure === undefined
79
+ ? {}
80
+ : {
81
+ brochure: {
82
+ ...doc.brochure,
83
+ title: render(doc.brochure.title, values),
84
+ body: render(doc.brochure.body, values),
85
+ },
86
+ }),
87
+ };
88
+ }
@@ -0,0 +1,34 @@
1
+ import { nearestFormat } from "./picture-size.js";
2
+ export const frames = {
3
+ a4: { width: 2480, height: 3508, pdf: { widthMm: 210, heightMm: 297 } },
4
+ a3: { width: 3508, height: 4961, pdf: { widthMm: 297, heightMm: 420 } },
5
+ flyer: { width: 1748, height: 2480, pdf: { widthMm: 148, heightMm: 210 } },
6
+ square: { width: 1080, height: 1080 },
7
+ portrait: { width: 1080, height: 1350 },
8
+ story: { width: 1080, height: 1920 },
9
+ wide: { width: 1920, height: 1080 },
10
+ // The brochure's cover is an A4 page; its inner pages are the document theme's own.
11
+ brochure: { width: 2480, height: 3508, pdf: { widthMm: 210, heightMm: 297 } },
12
+ };
13
+ // One artwork serves every format: the provider shape every chosen format is closest to, or
14
+ // square when they pull different ways, so each crop loses as little as it can.
15
+ export function artworkAspect(formats) {
16
+ const shapes = new Set(formats.map((format) => nearestFormat(frames[format])));
17
+ const [only] = shapes;
18
+ return shapes.size === 1 && only !== undefined ? only : "1:1";
19
+ }
20
+ // Cover-fit: the picture fills the frame, and what is cut off is cut around the focus point
21
+ // (0..1 on each axis of the picture), never past the picture's edge.
22
+ export function coverCrop(picture, frame, focus) {
23
+ const scale = Math.max(frame.width / picture.width, frame.height / picture.height);
24
+ const scaleWidth = Math.max(frame.width, Math.round(picture.width * scale));
25
+ const scaleHeight = Math.max(frame.height, Math.round(picture.height * scale));
26
+ // The frame is centred on the focus point, then slid back inside the picture.
27
+ const place = (size, window, at) => Math.min(size - window, Math.max(0, Math.round(at * size - window / 2)));
28
+ return {
29
+ scaleWidth,
30
+ scaleHeight,
31
+ x: place(scaleWidth, frame.width, focus.x),
32
+ y: place(scaleHeight, frame.height, focus.y),
33
+ };
34
+ }