@gentbajko/slopify 3.25.8 → 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 (604) 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/3.25.9.md +5 -0
  195. package/dist/patch-notes/4.0.0.md +51 -0
  196. package/dist/patch-notes/index.json +12 -0
  197. package/dist/slices/admission/heads.js +2 -1
  198. package/dist/slices/admission/repo.js +2 -1
  199. package/dist/slices/admission/rules.js +43 -8
  200. package/dist/slices/admission/schema.js +8 -0
  201. package/dist/slices/admission/stage-writes.js +0 -3
  202. package/dist/slices/admission/start.js +48 -31
  203. package/dist/slices/admission/substitute.js +7 -4
  204. package/dist/slices/agents/attention-posts.js +31 -0
  205. package/dist/slices/agents/attention-templates.js +33 -0
  206. package/dist/slices/agents/attention.js +10 -1
  207. package/dist/slices/agents/connection.js +2 -6
  208. package/dist/slices/agents/model.js +6 -3
  209. package/dist/slices/agents/repo.js +15 -7
  210. package/dist/slices/article/continuation.js +0 -63
  211. package/dist/slices/article/plain.js +3 -2
  212. package/dist/slices/article/segments.js +2 -62
  213. package/dist/slices/assistant/access.js +1 -0
  214. package/dist/slices/assistant/asks.js +80 -0
  215. package/dist/slices/assistant/cost.js +54 -0
  216. package/dist/slices/assistant/gate.js +222 -0
  217. package/dist/slices/assistant/model.js +79 -0
  218. package/dist/slices/assistant/names.js +23 -0
  219. package/dist/slices/assistant/prompt.js +74 -0
  220. package/dist/slices/assistant/repo.js +74 -0
  221. package/dist/slices/assistant/tokens.js +18 -0
  222. package/dist/slices/assistant/turn.js +342 -0
  223. package/dist/slices/autopilot/buffer.js +76 -0
  224. package/dist/slices/autopilot/model.js +14 -0
  225. package/dist/slices/autopilot/notify.js +27 -0
  226. package/dist/slices/autopilot/repo.js +34 -0
  227. package/dist/slices/autopilot/signals.js +194 -0
  228. package/dist/slices/autopilot/warnings.js +85 -0
  229. package/dist/slices/autopilot/weekly.js +100 -0
  230. package/dist/slices/backups/files.js +2 -6
  231. package/dist/slices/backups/repo.js +3 -15
  232. package/dist/slices/backups/service.js +4 -3
  233. package/dist/slices/channels/cast-images.js +2 -1
  234. package/dist/slices/channels/model.js +0 -1
  235. package/dist/slices/channels/rebrand.js +0 -3
  236. package/dist/slices/channels/repo.js +4 -0
  237. package/dist/slices/channels/schema.js +6 -0
  238. package/dist/slices/channels/service.js +10 -0
  239. package/dist/slices/channels/videos.js +2 -14
  240. package/dist/slices/checkpoints/index.js +0 -1
  241. package/dist/slices/control/providers.js +0 -26
  242. package/dist/slices/designs/artwork.js +66 -0
  243. package/dist/slices/designs/budget.js +23 -0
  244. package/dist/slices/designs/calls.js +124 -0
  245. package/dist/slices/designs/check-context.js +38 -0
  246. package/dist/slices/designs/check-text.js +43 -0
  247. package/dist/slices/designs/fields.js +88 -0
  248. package/dist/slices/designs/frames.js +34 -0
  249. package/dist/slices/designs/gate.js +128 -0
  250. package/dist/slices/designs/layout.js +293 -0
  251. package/dist/slices/designs/model.js +35 -0
  252. package/dist/slices/designs/pdf-job.js +23 -0
  253. package/dist/slices/designs/pdf-pages.js +30 -0
  254. package/dist/slices/designs/pdf-runner.js +68 -0
  255. package/dist/slices/designs/pdf-worker.js +24 -0
  256. package/dist/slices/designs/picture-size.js +79 -0
  257. package/dist/slices/designs/pictures.js +55 -0
  258. package/dist/slices/designs/quote.js +24 -0
  259. package/dist/slices/designs/render-args.js +36 -0
  260. package/dist/slices/designs/render-input.js +59 -0
  261. package/dist/slices/designs/render.js +215 -0
  262. package/dist/slices/designs/repo.js +101 -0
  263. package/dist/slices/designs/requests.js +80 -0
  264. package/dist/slices/designs/runner.js +239 -0
  265. package/dist/slices/designs/sample-repo.js +91 -0
  266. package/dist/slices/designs/sample.js +222 -0
  267. package/dist/slices/designs/schema.js +87 -0
  268. package/dist/slices/designs/sections.js +44 -0
  269. package/dist/slices/designs/service.js +233 -0
  270. package/dist/slices/designs/steps-repo.js +102 -0
  271. package/dist/slices/designs/studio-kind.js +39 -0
  272. package/dist/slices/designs/template-checks.js +44 -0
  273. package/dist/slices/designs/template-make.js +118 -0
  274. package/dist/slices/designs/templates.js +218 -0
  275. package/dist/slices/document/fonts.js +2 -1
  276. package/dist/slices/episodes/summarize.js +5 -1
  277. package/dist/slices/estimate/index.js +45 -7
  278. package/dist/slices/fonts/catalog.js +3 -2
  279. package/dist/slices/fonts/discovery.js +3 -2
  280. package/dist/slices/fonts/files.js +2 -6
  281. package/dist/slices/fonts/index.js +0 -1
  282. package/dist/slices/fonts/sfnt.js +45 -10
  283. package/dist/slices/images/appearance.js +6 -15
  284. package/dist/slices/images/scenes.js +100 -27
  285. package/dist/slices/keywords/digest.js +84 -0
  286. package/dist/slices/keywords/facts.js +92 -0
  287. package/dist/slices/keywords/fill.js +80 -0
  288. package/dist/slices/keywords/model.js +75 -0
  289. package/dist/slices/keywords/rules.js +187 -0
  290. package/dist/slices/keywords/schema.js +28 -0
  291. package/dist/slices/keywords/specs.js +260 -0
  292. package/dist/slices/keywords/values.js +74 -0
  293. package/dist/slices/library/lint.js +29 -12
  294. package/dist/slices/library/model.js +2 -0
  295. package/dist/slices/library/photorealistic.js +4 -12
  296. package/dist/slices/library/rename.js +9 -7
  297. package/dist/slices/library/save.js +14 -10
  298. package/dist/slices/library/slots.js +37 -11
  299. package/dist/slices/library/transfer.js +2 -1
  300. package/dist/slices/library/used-by.js +25 -10
  301. package/dist/slices/loudness/loudnorm.js +51 -11
  302. package/dist/slices/model-checks/cost.js +61 -0
  303. package/dist/slices/model-checks/listing.js +124 -0
  304. package/dist/slices/model-checks/model.js +20 -0
  305. package/dist/slices/model-checks/record.js +32 -0
  306. package/dist/slices/model-checks/repo.js +120 -0
  307. package/dist/slices/model-checks/rules.js +112 -0
  308. package/dist/slices/model-checks/run.js +143 -0
  309. package/dist/slices/model-checks/service.js +198 -0
  310. package/dist/slices/model-upkeep/switch.js +70 -25
  311. package/dist/slices/model-upkeep/usage.js +26 -11
  312. package/dist/slices/narration/breaths.js +2 -1
  313. package/dist/slices/narration/clean-pieces.js +2 -1
  314. package/dist/slices/narration/concat.js +2 -1
  315. package/dist/slices/narration/peaks.js +3 -4
  316. package/dist/slices/narration/pronunciation-chunks.js +4 -3
  317. package/dist/slices/narration/pronunciation.js +3 -2
  318. package/dist/slices/narration/steering.js +4 -3
  319. package/dist/slices/notifications/notifier.js +4 -3
  320. package/dist/slices/notifications/rules.js +5 -2
  321. package/dist/slices/notifications/settings.js +4 -12
  322. package/dist/slices/notifications/subject.js +2 -1
  323. package/dist/slices/onboarding/copy.js +2 -1
  324. package/dist/slices/onboarding/first-run.js +3 -2
  325. package/dist/slices/onboarding/full-video.js +3 -2
  326. package/dist/slices/onboarding/install.js +4 -3
  327. package/dist/slices/onboarding/model.js +0 -16
  328. package/dist/slices/onboarding/packs.js +22 -22
  329. package/dist/slices/onboarding/quick-short.js +2 -1
  330. package/dist/slices/onboarding/sample.js +0 -1
  331. package/dist/slices/onboarding/state.js +2 -11
  332. package/dist/slices/onboarding/template.js +2 -1
  333. package/dist/slices/patch-notes/library.js +3 -2
  334. package/dist/slices/play-drafts/convert.js +4 -0
  335. package/dist/slices/play-drafts/review-inputs.js +8 -2
  336. package/dist/slices/play-drafts/review.js +5 -4
  337. package/dist/slices/play-drafts/schema.js +4 -0
  338. package/dist/slices/project-templates/from-project.js +9 -2
  339. package/dist/slices/project-templates/repo.js +6 -1
  340. package/dist/slices/project-templates/setup.js +8 -0
  341. package/dist/slices/project-templates/stand-ins.js +22 -0
  342. package/dist/slices/rebuild/activity.js +2 -0
  343. package/dist/slices/rebuild/admission-repo.js +3 -2
  344. package/dist/slices/rebuild/narration-history.js +4 -5
  345. package/dist/slices/rebuild/narration-retry.js +2 -1
  346. package/dist/slices/rebuild/preview-details.js +27 -9
  347. package/dist/slices/rebuild/preview-plan.js +3 -8
  348. package/dist/slices/rebuild/recipe-build.js +8 -3
  349. package/dist/slices/rebuild/recipe-describe.js +3 -3
  350. package/dist/slices/rebuild/recipe-digest.js +58 -0
  351. package/dist/slices/rebuild/recipe-input-schema.js +10 -0
  352. package/dist/slices/rebuild/recipe-keywords.js +71 -0
  353. package/dist/slices/rebuild/recipe-legacy.js +5 -1
  354. package/dist/slices/rebuild/recipe-model.js +4 -0
  355. package/dist/slices/rebuild/recipe-narration-text.js +2 -1
  356. package/dist/slices/rebuild/recipe-preparation.js +3 -3
  357. package/dist/slices/rebuild/recipe-reference.js +8 -4
  358. package/dist/slices/rebuild/recipe-reviews.js +27 -7
  359. package/dist/slices/rebuild/recipe-save.js +6 -3
  360. package/dist/slices/rebuild/recipe-scenes.js +40 -11
  361. package/dist/slices/rebuild/recipe-shorts.js +8 -8
  362. package/dist/slices/rebuild/recipe-social.js +46 -0
  363. package/dist/slices/rebuild/recipe-text.js +64 -20
  364. package/dist/slices/rebuild/recipe-validation.js +98 -28
  365. package/dist/slices/rebuild/recipe-visual.js +55 -16
  366. package/dist/slices/rebuild/recipe-work.js +15 -3
  367. package/dist/slices/rebuild/recipe-youtube.js +4 -3
  368. package/dist/slices/rebuild/recovery-selection.js +3 -1
  369. package/dist/slices/rebuild/recovery.js +3 -2
  370. package/dist/slices/rebuild/repo.js +4 -3
  371. package/dist/slices/rebuild/review-promise.js +25 -0
  372. package/dist/slices/rebuild/review-redo.js +2 -1
  373. package/dist/slices/rebuild/runtime-admission.js +4 -3
  374. package/dist/slices/rebuild/runtime-agent-review.js +11 -5
  375. package/dist/slices/rebuild/runtime-animate.js +9 -8
  376. package/dist/slices/rebuild/runtime-article.js +3 -2
  377. package/dist/slices/rebuild/runtime-checkpoints.js +5 -4
  378. package/dist/slices/rebuild/runtime-document.js +8 -11
  379. package/dist/slices/rebuild/runtime-export-bed.js +6 -11
  380. package/dist/slices/rebuild/runtime-export-edit.js +6 -15
  381. package/dist/slices/rebuild/runtime-export-inputs.js +7 -7
  382. package/dist/slices/rebuild/runtime-export-short.js +8 -4
  383. package/dist/slices/rebuild/runtime-export.js +34 -12
  384. package/dist/slices/rebuild/runtime-figure-card.js +2 -1
  385. package/dist/slices/rebuild/runtime-image.js +4 -9
  386. package/dist/slices/rebuild/runtime-instructions.js +2 -0
  387. package/dist/slices/rebuild/runtime-keywords.js +59 -0
  388. package/dist/slices/rebuild/runtime-lines.js +2 -11
  389. package/dist/slices/rebuild/runtime-local.js +14 -20
  390. package/dist/slices/rebuild/runtime-narration-publication.js +2 -5
  391. package/dist/slices/rebuild/runtime-narration-reuse.js +9 -13
  392. package/dist/slices/rebuild/runtime-narration-text.js +10 -15
  393. package/dist/slices/rebuild/runtime-pictures.js +2 -4
  394. package/dist/slices/rebuild/runtime-piece-label.js +10 -6
  395. package/dist/slices/rebuild/runtime-plan.js +31 -5
  396. package/dist/slices/rebuild/runtime-provider.js +60 -19
  397. package/dist/slices/rebuild/runtime-publication.js +10 -9
  398. package/dist/slices/rebuild/runtime-review.js +33 -28
  399. package/dist/slices/rebuild/runtime-run.js +4 -1
  400. package/dist/slices/rebuild/runtime-shorts.js +34 -50
  401. package/dist/slices/rebuild/runtime-store.js +3 -8
  402. package/dist/slices/rebuild/runtime-subtitles.js +16 -14
  403. package/dist/slices/rebuild/runtime-voices.js +8 -15
  404. package/dist/slices/rebuild/runtime-youtube.js +11 -29
  405. package/dist/slices/rebuild/transition-repo.js +6 -5
  406. package/dist/slices/rebuild/work-records.js +6 -0
  407. package/dist/slices/reruns/cascade.js +1 -2
  408. package/dist/slices/reruns/index.js +6 -9
  409. package/dist/slices/reviews/external-model.js +3 -0
  410. package/dist/slices/reviews/external-repo.js +2 -1
  411. package/dist/slices/reviews/external-rows.js +2 -3
  412. package/dist/slices/reviews/model.js +26 -8
  413. package/dist/slices/reviews/repo.js +2 -1
  414. package/dist/slices/reviews/rules.js +1 -0
  415. package/dist/slices/reviews/view.js +5 -2
  416. package/dist/slices/revisions/downloads.js +4 -5
  417. package/dist/slices/revisions/mutation-assets.js +7 -4
  418. package/dist/slices/revisions/mutation-cues.js +2 -5
  419. package/dist/slices/revisions/mutations.js +0 -1
  420. package/dist/slices/revisions/repo.js +3 -1
  421. package/dist/slices/revisions/schema.js +1 -0
  422. package/dist/slices/revisions/view.js +20 -0
  423. package/dist/slices/run-cost/limits.js +5 -2
  424. package/dist/slices/run-cost/meter.js +12 -5
  425. package/dist/slices/run-cost/panel.js +8 -74
  426. package/dist/slices/run-cost/plan-readings.js +127 -0
  427. package/dist/slices/run-cost/plan-use.js +169 -0
  428. package/dist/slices/run-cost/pricing.js +3 -3
  429. package/dist/slices/run-cost/week.js +32 -4
  430. package/dist/slices/schedules/agenda.js +11 -2
  431. package/dist/slices/schedules/by-date.js +42 -0
  432. package/dist/slices/schedules/calendar.js +5 -0
  433. package/dist/slices/schedules/design-agenda.js +36 -0
  434. package/dist/slices/schedules/design-check.js +41 -0
  435. package/dist/slices/schedules/design-run.js +126 -0
  436. package/dist/slices/schedules/next-run.js +62 -0
  437. package/dist/slices/schedules/prepare.js +18 -2
  438. package/dist/slices/schedules/repo.js +24 -16
  439. package/dist/slices/schedules/rescore.js +35 -0
  440. package/dist/slices/schedules/scheduler.js +13 -3
  441. package/dist/slices/schedules/schema.js +54 -3
  442. package/dist/slices/schedules/score.js +107 -0
  443. package/dist/slices/schedules/service.js +57 -24
  444. package/dist/slices/schedules/topic-list.js +15 -0
  445. package/dist/slices/schedules/topic-scores.js +104 -0
  446. package/dist/slices/schedules/topics.js +48 -36
  447. package/dist/slices/settings/cli-paths.js +4 -6
  448. package/dist/slices/settings/first-run.js +2 -11
  449. package/dist/slices/settings/model-reach.js +3 -17
  450. package/dist/slices/settings/model.js +10 -0
  451. package/dist/slices/settings/playback.js +33 -22
  452. package/dist/slices/settings/probe-read.js +16 -0
  453. package/dist/slices/settings/repo.js +33 -0
  454. package/dist/slices/settings/tutorial.js +2 -2
  455. package/dist/slices/settings/voices.js +4 -12
  456. package/dist/slices/shorts/pick.js +2 -15
  457. package/dist/slices/shorts/prompts.js +2 -13
  458. package/dist/slices/shorts/render.js +3 -0
  459. package/dist/slices/social/channel.js +44 -0
  460. package/dist/slices/social/defaults.js +17 -0
  461. package/dist/slices/social/due.js +36 -0
  462. package/dist/slices/social/extension.js +18 -0
  463. package/dist/slices/social/fit.js +115 -0
  464. package/dist/slices/social/limits.js +186 -0
  465. package/dist/slices/social/links.js +21 -0
  466. package/dist/slices/social/media.js +131 -0
  467. package/dist/slices/social/model.js +75 -0
  468. package/dist/slices/social/notice.js +19 -0
  469. package/dist/slices/social/pack.js +176 -0
  470. package/dist/slices/social/post-prompt.js +55 -0
  471. package/dist/slices/social/queue.js +31 -0
  472. package/dist/slices/social/repo.js +133 -0
  473. package/dist/slices/social/reshape.js +97 -0
  474. package/dist/slices/social/rules.js +5 -0
  475. package/dist/slices/social/schema.js +70 -0
  476. package/dist/slices/social/serve.js +51 -0
  477. package/dist/slices/social/service.js +128 -0
  478. package/dist/slices/social/settings.js +21 -0
  479. package/dist/slices/social/sources.js +114 -0
  480. package/dist/slices/social/state.js +110 -0
  481. package/dist/slices/social/text.js +149 -0
  482. package/dist/slices/storage/arrange.js +3 -1
  483. package/dist/slices/storage/assets.js +8 -10
  484. package/dist/slices/storage/backup-designs.js +74 -0
  485. package/dist/slices/storage/backup-export.js +8 -3
  486. package/dist/slices/storage/backup-format.js +10 -0
  487. package/dist/slices/storage/backup-import.js +20 -2
  488. package/dist/slices/storage/delete-project.js +6 -5
  489. package/dist/slices/storage/files-location.js +6 -8
  490. package/dist/slices/storage/layout.js +13 -2
  491. package/dist/slices/storage/model.js +3 -0
  492. package/dist/slices/storage/portable.js +16 -11
  493. package/dist/slices/storage/schema.js +2 -0
  494. package/dist/slices/storage/staging-refs.js +2 -1
  495. package/dist/slices/storage/staging.js +6 -9
  496. package/dist/slices/storage/trim.js +10 -3
  497. package/dist/slices/studio/calendar.js +4 -1
  498. package/dist/slices/studio/pick.js +2 -11
  499. package/dist/slices/studio/plan.js +6 -15
  500. package/dist/slices/studio/queue.js +3 -14
  501. package/dist/slices/studio/releases.js +26 -0
  502. package/dist/slices/studio/settings.js +14 -51
  503. package/dist/slices/studio/videos.js +12 -0
  504. package/dist/slices/style-preview/render.js +34 -13
  505. package/dist/slices/style-preview/schema.js +13 -0
  506. package/dist/slices/style-preview/service.js +2 -1
  507. package/dist/slices/style-preview/settings.js +33 -5
  508. package/dist/slices/telemetry/collector-client.js +2 -4
  509. package/dist/slices/telemetry/flush.js +2 -4
  510. package/dist/slices/telemetry/model.js +0 -7
  511. package/dist/slices/telemetry/record.js +10 -3
  512. package/dist/slices/template-studio/anatomy-view.js +118 -0
  513. package/dist/slices/template-studio/anatomy.js +73 -0
  514. package/dist/slices/template-studio/bakeoff-make.js +225 -0
  515. package/dist/slices/template-studio/bakeoff-repo.js +83 -0
  516. package/dist/slices/template-studio/bakeoff.js +333 -0
  517. package/dist/slices/template-studio/budget.js +103 -0
  518. package/dist/slices/template-studio/checks.js +273 -0
  519. package/dist/slices/template-studio/document-sample.js +56 -0
  520. package/dist/slices/template-studio/edit.js +15 -0
  521. package/dist/slices/template-studio/for-all.js +161 -0
  522. package/dist/slices/template-studio/free-video.js +31 -0
  523. package/dist/slices/template-studio/free.js +176 -0
  524. package/dist/slices/template-studio/gate.js +40 -0
  525. package/dist/slices/template-studio/hidden.js +20 -0
  526. package/dist/slices/template-studio/jobs.js +79 -0
  527. package/dist/slices/template-studio/loudness.js +111 -0
  528. package/dist/slices/template-studio/make.js +40 -0
  529. package/dist/slices/template-studio/model.js +54 -0
  530. package/dist/slices/template-studio/pack-import.js +253 -0
  531. package/dist/slices/template-studio/pack-samples.js +60 -0
  532. package/dist/slices/template-studio/pack.js +205 -0
  533. package/dist/slices/template-studio/paid-view.js +66 -0
  534. package/dist/slices/template-studio/paid.js +348 -0
  535. package/dist/slices/template-studio/quote.js +148 -0
  536. package/dist/slices/template-studio/ready.js +104 -0
  537. package/dist/slices/template-studio/repo.js +134 -0
  538. package/dist/slices/template-studio/reviews-path.js +58 -0
  539. package/dist/slices/template-studio/runner.js +84 -0
  540. package/dist/slices/template-studio/save.js +236 -0
  541. package/dist/slices/template-studio/say-it-as.js +248 -0
  542. package/dist/slices/template-studio/schedule-gate.js +34 -0
  543. package/dist/slices/template-studio/schedule-price.js +30 -0
  544. package/dist/slices/template-studio/schedule-sample.js +87 -0
  545. package/dist/slices/template-studio/schema.js +218 -0
  546. package/dist/slices/template-studio/scope.js +124 -0
  547. package/dist/slices/template-studio/sections.js +176 -0
  548. package/dist/slices/template-studio/service.js +173 -0
  549. package/dist/slices/template-studio/status.js +174 -0
  550. package/dist/slices/template-studio/topic.js +223 -0
  551. package/dist/slices/trash/designs.js +76 -0
  552. package/dist/slices/trash/model.js +13 -4
  553. package/dist/slices/trash/service.js +52 -2
  554. package/dist/slices/tutorials/library.js +3 -2
  555. package/dist/slices/video/ass-text.js +17 -0
  556. package/dist/slices/video/audio-export.js +2 -64
  557. package/dist/slices/video/encoding.js +160 -0
  558. package/dist/slices/video/ffmpeg.js +23 -21
  559. package/dist/slices/video/figure-card.js +1 -15
  560. package/dist/slices/video/slideshow.js +43 -18
  561. package/dist/slices/voices/palette.js +0 -3
  562. package/dist/slices/youtube/answer.js +2 -15
  563. package/dist/slices/youtube/edits-repo.js +2 -11
  564. package/dist/tutorials/Agents-MCP.md +2 -1
  565. package/dist/tutorials/Assistant.md +42 -0
  566. package/dist/tutorials/Channels.md +2 -0
  567. package/dist/tutorials/Home-and-Projects.md +19 -0
  568. package/dist/tutorials/Install.md +1 -0
  569. package/dist/tutorials/Keywords-Slopify-Fills.md +87 -0
  570. package/dist/tutorials/Models.md +33 -1
  571. package/dist/tutorials/Notifications.md +6 -0
  572. package/dist/tutorials/Play-Images.md +4 -0
  573. package/dist/tutorials/Posting-to-Social-Sites.md +121 -0
  574. package/dist/tutorials/Prompts.md +10 -6
  575. package/dist/tutorials/Remote-Access.md +63 -0
  576. package/dist/tutorials/Reviews-and-Checkpoints.md +13 -2
  577. package/dist/tutorials/Schedules.md +21 -1
  578. package/dist/tutorials/Settings-Reference.md +50 -8
  579. package/dist/tutorials/Template-Studio.md +125 -0
  580. package/dist/tutorials/Templates.md +3 -0
  581. package/dist/tutorials/_Sidebar.md +5 -0
  582. package/dist/updater/work-in-progress.js +42 -12
  583. package/dist/web/assets/index-CkAI_2E7.css +1 -0
  584. package/dist/web/assets/index-DcD0cdfo.js +334 -0
  585. package/dist/web/assets/{pdf-T7MCwjIv.js → pdf-TODRwF31.js} +1 -1
  586. package/dist/web/index.html +2 -2
  587. package/package.json +5 -1
  588. package/dist/slices/article/provided-entries.js +0 -66
  589. package/dist/slices/article/run.js +0 -145
  590. package/dist/slices/images/run.js +0 -234
  591. package/dist/slices/narration/legacy-plan.js +0 -58
  592. package/dist/slices/narration/run.js +0 -270
  593. package/dist/slices/rebuild/runtime-actions.js +0 -90
  594. package/dist/slices/research/run.js +0 -224
  595. package/dist/slices/subtitles/prepare.js +0 -194
  596. package/dist/slices/subtitles/transcript.js +0 -34
  597. package/dist/slices/thumbnail/run.js +0 -219
  598. package/dist/slices/video/audio-inputs.js +0 -33
  599. package/dist/slices/video/reuse-audio.js +0 -62
  600. package/dist/slices/video/run.js +0 -78
  601. package/dist/slices/video/write-export.js +0 -158
  602. package/dist/slices/video/write-subtitles.js +0 -59
  603. package/dist/web/assets/index-BadsoORk.css +0 -1
  604. package/dist/web/assets/index-D8LqNsCl.js +0 -326
@@ -0,0 +1,218 @@
1
+ import { z } from "zod";
2
+ import { transact } from "../../kernel/db/tx.js";
3
+ import { defaultChannelId } from "../channels/model.js";
4
+ import { designDocumentSchema } from "./schema.js";
5
+ import { mutationHash, reusedMessage } from "./service.js";
6
+ import { documentChecks } from "./template-checks.js";
7
+ // Design templates: a designed piece saved with fields to make again by hand or from a
8
+ // schedule. Every Save is a new version; a version is marked ready only when its checks pass,
9
+ // and only a ready version is what a schedule makes (`runnableDesignVersion`).
10
+ const id = z
11
+ .string()
12
+ .min(1)
13
+ .max(64)
14
+ .regex(/^[A-Za-z0-9_-]+$/);
15
+ const name = z.string().trim().min(1).max(200);
16
+ export const createTemplateSchema = z
17
+ .object({
18
+ id,
19
+ mutationId: z.string().min(8).max(128),
20
+ name,
21
+ document: designDocumentSchema,
22
+ channelId: id.optional(),
23
+ })
24
+ .strict();
25
+ export const saveTemplateSchema = z
26
+ .object({
27
+ baseVersion: z.number().int().min(1),
28
+ name,
29
+ document: designDocumentSchema,
30
+ // Whether this Save redraws the editor's sample (`sample.ts`); "skip" saves only.
31
+ sample: z.enum(["make", "skip"]).optional(),
32
+ acknowledgedHigh: z.number().min(0).max(1000).optional(),
33
+ })
34
+ .strict();
35
+ export const markReadySchema = z.object({ version: z.number().int().min(1) }).strict();
36
+ const revisionRow = z.object({
37
+ version: z.number(),
38
+ name: z.string(),
39
+ document_json: z.string(),
40
+ gate: z.enum(["unchecked", "ready"]),
41
+ checks_json: z.string().nullable(),
42
+ marked_at: z.string().nullable(),
43
+ created_at: z.string(),
44
+ });
45
+ const checksSchema = z
46
+ .array(z.object({
47
+ id: z.string(),
48
+ ok: z.boolean(),
49
+ sentence: z.string(),
50
+ fix: z.object({ label: z.string(), href: z.string() }).nullable(),
51
+ }))
52
+ .catch([]);
53
+ export function templateRevision(db, templateId, version) {
54
+ const raw = db
55
+ .prepare("SELECT * FROM design_template_revisions WHERE template_id=? AND version=?")
56
+ .get(templateId, version);
57
+ if (raw === undefined)
58
+ return undefined;
59
+ const row = revisionRow.parse(raw);
60
+ return {
61
+ version: row.version,
62
+ name: row.name,
63
+ gate: row.gate,
64
+ markedAt: row.marked_at,
65
+ createdAt: row.created_at,
66
+ document: designDocumentSchema.parse(JSON.parse(row.document_json)),
67
+ };
68
+ }
69
+ export function getTemplate(db, templateId) {
70
+ const head = db
71
+ .prepare("SELECT id, channel_id, head_version, created_at FROM design_templates WHERE id=? AND deleted_at IS NULL")
72
+ .get(templateId);
73
+ if (head === undefined)
74
+ return undefined;
75
+ const revisions = db
76
+ .prepare("SELECT * FROM design_template_revisions WHERE template_id=? ORDER BY version")
77
+ .all(templateId)
78
+ .map((raw) => revisionRow.parse(raw));
79
+ const current = revisions.find((row) => row.version === head.head_version);
80
+ if (current === undefined)
81
+ return undefined;
82
+ const ready = revisions.filter((row) => row.gate === "ready").at(-1);
83
+ return {
84
+ id: String(head.id),
85
+ channelId: head.channel_id === null ? defaultChannelId : String(head.channel_id),
86
+ headVersion: current.version,
87
+ name: current.name,
88
+ document: designDocumentSchema.parse(JSON.parse(current.document_json)),
89
+ gate: current.gate,
90
+ checks: current.checks_json === null ? [] : checksSchema.parse(JSON.parse(current.checks_json)),
91
+ readyVersion: ready?.version ?? null,
92
+ versions: revisions.map((row) => ({
93
+ version: row.version,
94
+ name: row.name,
95
+ gate: row.gate,
96
+ markedAt: row.marked_at,
97
+ createdAt: row.created_at,
98
+ })),
99
+ createdAt: String(head.created_at),
100
+ updatedAt: current.created_at,
101
+ };
102
+ }
103
+ export function listTemplates(db) {
104
+ return db
105
+ .prepare("SELECT id FROM design_templates WHERE deleted_at IS NULL ORDER BY created_at DESC, id")
106
+ .all()
107
+ .flatMap((row) => getTemplate(db, String(row.id)) ?? []);
108
+ }
109
+ const notFound = {
110
+ ok: false,
111
+ reason: "not-found",
112
+ message: "This design template no longer exists. Open Library → Designs → Templates to choose another.",
113
+ };
114
+ function invalid(error) {
115
+ return {
116
+ ok: false,
117
+ reason: "invalid",
118
+ message: `The template can't be saved: ${error.issues[0]?.message ?? "a field is missing"}.`,
119
+ };
120
+ }
121
+ export function createTemplate(deps, raw) {
122
+ const parsed = createTemplateSchema.safeParse(raw);
123
+ if (!parsed.success)
124
+ return invalid(parsed.error);
125
+ const { mutationId, ...input } = parsed.data;
126
+ const hash = mutationHash(input);
127
+ const replay = deps.db
128
+ .prepare("SELECT id, mutation_hash FROM design_templates WHERE mutation_id=?")
129
+ .get(mutationId);
130
+ if (replay !== undefined) {
131
+ if (replay.mutation_hash !== hash)
132
+ return { ok: false, reason: "mutation-reused", message: reusedMessage };
133
+ const template = getTemplate(deps.db, String(replay.id));
134
+ return template === undefined ? notFound : { ok: true, value: template };
135
+ }
136
+ if (deps.db.prepare("SELECT 1 FROM design_templates WHERE id=?").get(input.id) !== undefined)
137
+ return { ok: false, reason: "mutation-reused", message: reusedMessage };
138
+ const at = deps.clock.now().toISOString();
139
+ transact(deps.db, () => {
140
+ deps.db
141
+ .prepare("INSERT INTO design_templates (id,channel_id,head_version,created_at,mutation_id,mutation_hash) VALUES (?,?,1,?,?,?)")
142
+ .run(input.id, input.channelId ?? null, at, mutationId, hash);
143
+ deps.db
144
+ .prepare("INSERT INTO design_template_revisions (template_id,version,name,document_json,created_at) VALUES (?,1,?,?,?)")
145
+ .run(input.id, input.name, JSON.stringify(input.document), at);
146
+ });
147
+ const template = getTemplate(deps.db, input.id);
148
+ return template === undefined ? notFound : { ok: true, value: template };
149
+ }
150
+ // A Save on the version it was made on becomes the next version; one made on an older version
151
+ // is refused with the current template. Saving exactly what the head already holds is a no-op.
152
+ export function saveTemplate(deps, templateId, raw) {
153
+ const parsed = saveTemplateSchema.safeParse(raw);
154
+ if (!parsed.success)
155
+ return invalid(parsed.error);
156
+ const current = getTemplate(deps.db, templateId);
157
+ if (current === undefined)
158
+ return notFound;
159
+ const input = parsed.data;
160
+ const same = input.name === current.name &&
161
+ JSON.stringify(input.document) === JSON.stringify(current.document);
162
+ if (same)
163
+ return { ok: true, value: { template: current, saved: false, before: current.document } };
164
+ if (input.baseVersion !== current.headVersion)
165
+ return {
166
+ ok: false,
167
+ reason: "changed",
168
+ message: "This template was saved in another tab or by an agent. Reload it to see that version, then make your change again.",
169
+ current,
170
+ };
171
+ const version = current.headVersion + 1;
172
+ transact(deps.db, () => {
173
+ deps.db
174
+ .prepare("INSERT INTO design_template_revisions (template_id,version,name,document_json,created_at) VALUES (?,?,?,?,?)")
175
+ .run(templateId, version, input.name, JSON.stringify(input.document), deps.clock.now().toISOString());
176
+ deps.db
177
+ .prepare("UPDATE design_templates SET head_version=? WHERE id=?")
178
+ .run(version, templateId);
179
+ });
180
+ const template = getTemplate(deps.db, templateId);
181
+ return template === undefined
182
+ ? notFound
183
+ : { ok: true, value: { template, saved: true, before: current.document } };
184
+ }
185
+ // Mark ready: the version's checks are run and kept; it is marked only when every one passes,
186
+ // and otherwise refused with the list.
187
+ export async function markTemplateReady(deps, templateId, raw, more = async () => []) {
188
+ const parsed = markReadySchema.safeParse(raw);
189
+ if (!parsed.success)
190
+ return { ok: false, reason: "invalid", message: "Say which version to mark ready." };
191
+ if (getTemplate(deps.db, templateId) === undefined)
192
+ return notFound;
193
+ const revision = templateRevision(deps.db, templateId, parsed.data.version);
194
+ if (revision === undefined)
195
+ return {
196
+ ok: false,
197
+ reason: "not-found",
198
+ message: "That version of the template doesn't exist. Reload the template.",
199
+ };
200
+ const checks = [
201
+ ...documentChecks(revision.document, templateId),
202
+ ...(await more(revision.document, parsed.data.version)),
203
+ ];
204
+ const passed = checks.every((check) => check.ok);
205
+ deps.db
206
+ .prepare("UPDATE design_template_revisions SET checks_json=?, gate=?, marked_at=? WHERE template_id=? AND version=?")
207
+ .run(JSON.stringify(checks), passed ? "ready" : revision.gate, passed ? deps.clock.now().toISOString() : revision.markedAt, templateId, parsed.data.version);
208
+ const failed = checks.filter((check) => !check.ok);
209
+ if (failed.length > 0)
210
+ return {
211
+ ok: false,
212
+ reason: "not-ready",
213
+ message: `Version ${String(parsed.data.version)} can't be marked ready yet: ${failed.map((check) => check.sentence).join(" ")}`,
214
+ current: failed,
215
+ };
216
+ const template = getTemplate(deps.db, templateId);
217
+ return template === undefined ? notFound : { ok: true, value: template };
218
+ }
@@ -1,4 +1,5 @@
1
1
  import { readFileSync } from "node:fs";
2
+ import { errorText } from "../../kernel/errors.js";
2
3
  // The bundled Cinzel weights and Literata styles (SIL OFL 1.1, assets/document/fonts) and the
3
4
  // page texture. Read on every render rather than kept in memory: eight fonts and one image
4
5
  // are about a megabyte and a half, and a document is made once per article.
@@ -32,7 +33,7 @@ export function readDocumentAssets() {
32
33
  };
33
34
  }
34
35
  catch (error) {
35
- throw new Error(`Slopify couldn't read the fonts and page texture it makes documents with (${error instanceof Error ? error.message : String(error)}). Its installation is incomplete: reinstall or update Slopify, then use Try again on Document.`, { cause: error });
36
+ throw new Error(`Slopify couldn't read the fonts and page texture it makes documents with (${errorText(error)}). Its installation is incomplete: reinstall or update Slopify, then use Try again on Document.`, { cause: error });
36
37
  }
37
38
  }
38
39
  export function registerFonts(doc, fonts) {
@@ -1,11 +1,13 @@
1
1
  import { createHash } from "node:crypto";
2
2
  import { existsSync, readFileSync } from "node:fs";
3
+ import { errorText } from "../../kernel/errors.js";
3
4
  import { projectById } from "../admission/repo.js";
4
5
  import { castMentions } from "../channels/cast-match.js";
5
6
  import { castOfChannel, projectChannelId } from "../channels/repo.js";
6
7
  import { reviewedByAgent } from "../reviews/model.js";
7
8
  import { outputPath } from "../storage/layout.js";
8
9
  import { outputsOf } from "../storage/repo.js";
10
+ import { isTemplateSample } from "../template-studio/repo.js";
9
11
  import { episodeMemoryOn, memoryOfProject, saveGeneratedMemory } from "./repo.js";
10
12
  // When a project finishes, a small LLM call writes what the episode covered (at most
11
13
  // `summaryWordsMax` words) for its channel's episode memory. It never fails the project: every
@@ -51,7 +53,7 @@ export async function summarizeEpisode(deps, projectId, signal) {
51
53
  return "failed";
52
54
  deps.log.write("warn", "episode.memory", {
53
55
  projectId,
54
- detail: `The episode summary wasn't saved: ${error instanceof Error ? error.message : String(error)}. The project is unaffected; only later episodes won't be reminded of this one. Check the project's text model in Settings → Providers.`,
56
+ detail: `The episode summary wasn't saved: ${errorText(error)}. The project is unaffected; only later episodes won't be reminded of this one. Check the project's text model in Settings → Providers.`,
55
57
  });
56
58
  return "failed";
57
59
  }
@@ -60,6 +62,8 @@ async function summarize(deps, projectId, signal) {
60
62
  const project = projectById(deps.db, projectId);
61
63
  if (project === undefined)
62
64
  return "gone";
65
+ if (isTemplateSample(deps.db, projectId))
66
+ return "sample";
63
67
  const channelId = projectChannelId(deps.db, projectId);
64
68
  if (!episodeMemoryOn(deps.db, channelId))
65
69
  return "off";
@@ -3,7 +3,10 @@ import { sourceOf, thumbnailCountOf } from "../admission/model.js";
3
3
  import { imageSecondsProblem, usesDescribedNarration, usesNarrationPreparation, usesReference, usesShorts, usesYoutubeDescription, } from "../admission/rules.js";
4
4
  import { plainText } from "../article/plain.js";
5
5
  import { splitEndMatter } from "../article/split.js";
6
+ import { usesAppearance } from "../images/appearance.js";
6
7
  import { narrationMinutes, plannedImageCount, scalesImages } from "../images/scale.js";
8
+ import { usesSlopifyScene } from "../images/scenes.js";
9
+ import { keywordUsesOf, placeOfKey } from "../keywords/rules.js";
7
10
  import { describedBlocks, narrationBlocks } from "../narration/blocks.js";
8
11
  import { chunkNarration, defaultChunking } from "../narration/chunk.js";
9
12
  import { describeMessages } from "../narration/describe.js";
@@ -13,12 +16,15 @@ import { defaultReviewPrompts, reviewedByAgent, reviewPromptKey, reviewRetriesOf
13
16
  import { activeReviewStages } from "../reviews/rules.js";
14
17
  import { isLocalCliProvider } from "../settings/model.js";
15
18
  import { defaultShortsImagePrompt, defaultShortsPrompt, shortsImageUpperBound, shortsSettingsProblems, } from "../shorts/model.js";
19
+ import { socialPlatforms } from "../social/model.js";
20
+ import { defaultPostPrompt } from "../social/post-prompt.js";
16
21
  import { animatedClipSeconds, animatedImageIndexes, usesAnimation, videoEditProblems, } from "../video/edit-settings.js";
17
- import { usesVoices } from "../voices/model.js";
22
+ import { usesScriptPrompt, usesVoices } from "../voices/model.js";
18
23
  import { defaultDescriptionPrompt } from "../youtube/model.js";
19
24
  // ceiling: the most chapters "Chapter openers" is charged for before the article exists, the
20
25
  // top of the 5-12 chapters the built-in YouTube description prompt asks for.
21
26
  const chapterOpenersMax = 12;
27
+ import { sampleSpokenWords } from "../template-studio/scope.js";
22
28
  import { estimateRequests, groupEstimateRows } from "./requests.js";
23
29
  export { estimateRequests } from "./requests.js";
24
30
  export function estimateRun(draft, rendered, expectedWords, catalogue) {
@@ -55,7 +61,8 @@ export function estimateRun(draft, rendered, expectedWords, catalogue) {
55
61
  key === "description" ||
56
62
  key === "shorts" ||
57
63
  key === "shortsImage" ||
58
- key.startsWith("review.")
64
+ key.startsWith("review.") ||
65
+ key.startsWith("social.")
59
66
  ? 0
60
67
  : value.length), 0);
61
68
  const local = (stage, detail) => {
@@ -127,6 +134,8 @@ export function estimateRun(draft, rendered, expectedWords, catalogue) {
127
134
  detail: "TTS character charges include delivery tags; their length is known after preparation.",
128
135
  });
129
136
  }
137
+ // A template studio sample says only the narration's opening (`template-studio/scope.ts`).
138
+ const spokenChars = draft.sample === undefined ? articleChars : Math.min(articleChars, sampleSpokenWords * 6);
130
139
  const extras = ["intro", "outro"].reduce((n, key) => n + (rendered[key]?.length ?? 0), 0);
131
140
  const llmExtras = (draft.intro?.mode === "llm" ? 1200 : 0) + (draft.outro?.mode === "llm" ? 1200 : 0);
132
141
  const choice = { stage: "Narration", provider: tts.provider, model: tts.model };
@@ -140,7 +149,7 @@ export function estimateRun(draft, rendered, expectedWords, catalogue) {
140
149
  stage: "Narration",
141
150
  provider: speaker.voice.provider,
142
151
  model: speaker.voice.model,
143
- characters: Math.round(articleChars / speakers.length),
152
+ characters: Math.round(spokenChars / speakers.length),
144
153
  detail: `${speaker.name.trim() || "A speaker"}: assumes every speaker says about the same.`,
145
154
  });
146
155
  if (extras + llmExtras > 0)
@@ -152,7 +161,7 @@ export function estimateRun(draft, rendered, expectedWords, catalogue) {
152
161
  }
153
162
  else
154
163
  requests.push(generatedArticle || llmExtras > 0
155
- ? { ...choice, kind: "tts-estimate", characters: articleChars + extras + llmExtras }
164
+ ? { ...choice, kind: "tts-estimate", characters: spokenChars + extras + llmExtras }
156
165
  : {
157
166
  ...choice,
158
167
  kind: "tts",
@@ -178,9 +187,29 @@ export function estimateRun(draft, rendered, expectedWords, catalogue) {
178
187
  });
179
188
  if (images === 0)
180
189
  local("Images", "Provided or off.");
181
- // Scenes from the article: one call reads the article and writes every image's scene.
182
- if (images > 0 && draft.imageScenes === true)
190
+ // Scenes from the article: one call reads the article and writes every image's scene. A
191
+ // `Slopify_` scene keyword turns it on for the pictures that use it, switch or no switch.
192
+ const pictureKeys = Object.keys(rendered).filter((key) => /^imagePrompts\.\d+$/.test(key));
193
+ if (images > 0 &&
194
+ (draft.imageScenes === true || pictureKeys.some((key) => usesSlopifyScene(rendered[key]))))
183
195
  text("Image scenes", articleChars + 1500, 120 * images);
196
+ if (draft.sources.thumbnail === "from_prompt" && usesSlopifyScene(rendered.thumbnailPrompt))
197
+ text("Thumbnail scenes", articleChars + 1500, 120 * thumbnailCountOf(draft));
198
+ // The keyword digest: one call reads the article and writes every digest keyword.
199
+ const placed = Object.entries(rendered).flatMap(([key, body]) => {
200
+ const place = placeOfKey(key, { script: usesScriptPrompt(draft) });
201
+ return place === undefined ? [] : [{ field: key, place, body }];
202
+ });
203
+ if (keywordUsesOf(placed).digest !== undefined)
204
+ text("Keyword digest", articleChars + 1500, 600, "One call reads the article and writes every digest keyword the run uses.");
205
+ // `{{Appearance}}` (or `{{Slopify_Appearance}}`): one call with web search looks up the looks.
206
+ if ([
207
+ ...(images > 0 ? pictureKeys : []),
208
+ ...(draft.sources.thumbnail === "from_prompt" ? ["thumbnailPrompt"] : []),
209
+ "referencePrompt",
210
+ "shortsImage",
211
+ ].some((key) => usesAppearance(rendered[key])))
212
+ text("Appearance lookup", articleChars + 1500, 1500, "One call with web search looks up how the subject and characters look.");
184
213
  // The establishing image is one more image when it is made from a prompt.
185
214
  if (usesReference(draft) && draft.reference?.source === "prompt")
186
215
  requests.push({
@@ -210,6 +239,13 @@ export function estimateRun(draft, rendered, expectedWords, catalogue) {
210
239
  : defaultDescriptionPrompt.length) +
211
240
  Math.round(articleChars * 1.05) +
212
241
  1500, 2400, "One LLM call on the timed transcript.");
242
+ // Each site whose post is written by a post prompt: one call that reads the article.
243
+ for (const platform of socialPlatforms) {
244
+ if (draft.social?.[platform]?.text !== "prompt")
245
+ continue;
246
+ const own = rendered[`social.${platform}`]?.trim() ?? "";
247
+ text("Posts", (own === "" ? defaultPostPrompt.length : own.length) + articleChars + 400, 600, "One LLM call per site whose post a post prompt writes, reading the article.");
248
+ }
213
249
  // Shorts: one call picks the clips from the numbered transcript, one per clip writes its
214
250
  // image prompts, and the images are charged at the most the step can ask for, every clip
215
251
  // at the longest length allowed.
@@ -273,7 +309,9 @@ export function estimateRun(draft, rendered, expectedWords, catalogue) {
273
309
  ? articleChars + promptChars
274
310
  : stage === "narration"
275
311
  ? Math.round(articleChars * 2.1)
276
- : 1500;
312
+ : stage === "promise"
313
+ ? articleChars + 1500
314
+ : 1500;
277
315
  for (let index = 0; index < items; index++)
278
316
  requests.push({
279
317
  kind: "llm",
@@ -2,8 +2,9 @@ import { createHash } from "node:crypto";
2
2
  import { lstat, readdir } from "node:fs/promises";
3
3
  import { join } from "node:path";
4
4
  import { fileURLToPath } from "node:url";
5
+ import { isUnreachablePath } from "../../kernel/errors.js";
5
6
  import { discoverSystemFonts } from "./discovery.js";
6
- import { readFontFile, unavailable } from "./files.js";
7
+ import { readFontFile } from "./files.js";
7
8
  import { fontSummary } from "./model.js";
8
9
  import { readFontMetadata } from "./sfnt.js";
9
10
  export const fontIdPattern = /^(?:default|(?:system|uploaded)-[a-f0-9]{64})$/;
@@ -86,7 +87,7 @@ async function uploadedFonts(paths, wanted) {
86
87
  names = wanted === undefined ? await readdir(root) : [`${wanted}.ttf`, `${wanted}.otf`];
87
88
  }
88
89
  catch (error) {
89
- if (unavailable(error))
90
+ if (isUnreachablePath(error))
90
91
  return [];
91
92
  throw error;
92
93
  }
@@ -2,7 +2,8 @@ import { createHash } from "node:crypto";
2
2
  import { opendir } from "node:fs/promises";
3
3
  import { homedir } from "node:os";
4
4
  import { extname, join, posix, win32 } from "node:path";
5
- import { readFontFile, unavailable } from "./files.js";
5
+ import { isUnreachablePath } from "../../kernel/errors.js";
6
+ import { readFontFile } from "./files.js";
6
7
  import { readFontMetadata } from "./sfnt.js";
7
8
  export function systemFontDirectories(platform, env, home) {
8
9
  if (platform === "win32")
@@ -46,7 +47,7 @@ export async function scanFontDirectories(roots) {
46
47
  }
47
48
  }
48
49
  catch (error) {
49
- if (!unavailable(error))
50
+ if (!isUnreachablePath(error))
50
51
  throw error;
51
52
  }
52
53
  }
@@ -1,11 +1,7 @@
1
1
  import { constants } from "node:fs";
2
2
  import { lstat, open } from "node:fs/promises";
3
+ import { isUnreachablePath } from "../../kernel/errors.js";
3
4
  import { fontMaxBytes } from "./model.js";
4
- export function unavailable(error) {
5
- return (error instanceof Error &&
6
- "code" in error &&
7
- ["ENOENT", "ENOTDIR", "EACCES", "EPERM", "ELOOP"].includes(String(error.code)));
8
- }
9
5
  export async function readFontFile(path) {
10
6
  try {
11
7
  if (!(await lstat(path)).isFile())
@@ -30,7 +26,7 @@ export async function readFontFile(path) {
30
26
  }
31
27
  }
32
28
  catch (error) {
33
- if (unavailable(error))
29
+ if (isUnreachablePath(error))
34
30
  return undefined;
35
31
  throw error;
36
32
  }
@@ -1,5 +1,4 @@
1
1
  export { fontIdPattern, isMissingFont, listFonts, resolveBoldFont, resolveFont, } from "./catalog.js";
2
2
  export { fontMaxBytes } from "./model.js";
3
3
  export { previewFont } from "./preview.js";
4
- export { standaloneFont } from "./sfnt.js";
5
4
  export { uploadFont } from "./upload.js";
@@ -266,8 +266,50 @@ function checksum(bytes) {
266
266
  export function mappedCodepoints(content, index, codepoints) {
267
267
  const bytes = Buffer.from(content.buffer, content.byteOffset, content.byteLength);
268
268
  const face = directories(bytes)?.[index];
269
- const table = face?.tables.get("cmap");
270
- if (face === undefined || table === undefined || !validCmap(bytes, table))
269
+ const cmap = face === undefined ? undefined : unicodeCmap(bytes, face);
270
+ if (cmap === undefined)
271
+ return undefined;
272
+ const found = new Set();
273
+ for (const codepoint of codepoints)
274
+ if (glyphOf(bytes, cmap.at, cmap.format, codepoint, cmap.end) !== 0)
275
+ found.add(codepoint);
276
+ return found;
277
+ }
278
+ // How wide each character is drawn, as a share of the ASS font size: the glyph's advance
279
+ // (hmtx) over the height libass sizes a font by (OS/2 winAscent + winDescent, else the em).
280
+ // Undefined for a character the font has no glyph for, and the whole answer undefined when
281
+ // the font's tables can't be read; the caller then estimates.
282
+ export function fontAdvances(content, index) {
283
+ const bytes = Buffer.from(content.buffer, content.byteOffset, content.byteLength);
284
+ const face = directories(bytes)?.[index];
285
+ const cmap = face === undefined ? undefined : unicodeCmap(bytes, face);
286
+ const head = face?.tables.get("head");
287
+ const hhea = face?.tables.get("hhea");
288
+ const hmtx = face?.tables.get("hmtx");
289
+ if (cmap === undefined || head === undefined || hhea === undefined || hmtx === undefined)
290
+ return undefined;
291
+ const units = bytes.readUInt16BE(head.offset + 18);
292
+ const metrics = bytes.readUInt16BE(hhea.offset + 34);
293
+ const os2 = face?.tables.get("OS/2");
294
+ const win = os2 !== undefined && os2.length >= 78
295
+ ? bytes.readUInt16BE(os2.offset + 74) + bytes.readUInt16BE(os2.offset + 76)
296
+ : 0;
297
+ const height = win >= units / 2 && win <= units * 4 ? win : units;
298
+ const cache = new Map();
299
+ return (codepoint) => {
300
+ if (cache.has(codepoint))
301
+ return cache.get(codepoint);
302
+ const glyph = glyphOf(bytes, cmap.at, cmap.format, codepoint, cmap.end);
303
+ // Glyphs past the last full metric share its advance (validated in `validTables`).
304
+ const advance = glyph === 0 ? undefined : bytes.readUInt16BE(hmtx.offset + Math.min(glyph, metrics - 1) * 4);
305
+ const share = advance === undefined ? undefined : advance / height;
306
+ cache.set(codepoint, share);
307
+ return share;
308
+ };
309
+ }
310
+ function unicodeCmap(bytes, face) {
311
+ const table = face.tables.get("cmap");
312
+ if (table === undefined || !validCmap(bytes, table))
271
313
  return undefined;
272
314
  const count = bytes.readUInt16BE(table.offset + 2);
273
315
  let best;
@@ -283,14 +325,7 @@ export function mappedCodepoints(content, index, codepoints) {
283
325
  if (best === undefined || format > best.format)
284
326
  best = { at, format };
285
327
  }
286
- if (best === undefined)
287
- return undefined;
288
- const end = table.offset + table.length;
289
- const found = new Set();
290
- for (const codepoint of codepoints)
291
- if (glyphOf(bytes, best.at, best.format, codepoint, end) !== 0)
292
- found.add(codepoint);
293
- return found;
328
+ return best === undefined ? undefined : { ...best, end: table.offset + table.length };
294
329
  }
295
330
  function glyphOf(bytes, at, format, codepoint, end) {
296
331
  if (format === 12) {
@@ -1,12 +1,15 @@
1
1
  import { z } from "zod";
2
+ import { jsonBetween } from "../../kernel/json-answer.js";
2
3
  import { appearanceKeyword, detectSlots, render } from "../admission/substitute.js";
3
4
  // How things look, looked up rather than guessed: when an image or thumbnail prompt has
4
5
  // `{{Appearance}}`, one call to the project's AI model searches the web for how the video's
5
6
  // subject and every named character in the article look in their best-known depiction. Each
6
7
  // picture then gets the subject's look and the looks of the characters its scene names, so a
7
8
  // figure looks the same from image to image and like the one people know.
9
+ // `{{Appearance}}` is the old name of `{{Slopify_Appearance}}`; both mean the same.
10
+ const appearanceNames = [appearanceKeyword, "Slopify_Appearance"];
8
11
  export function usesAppearance(body) {
9
- return body != null && detectSlots(body).names.includes(appearanceKeyword);
12
+ return body != null && detectSlots(body).names.some((name) => appearanceNames.includes(name));
10
13
  }
11
14
  const figureSchema = z.object({
12
15
  name: z.string(),
@@ -42,7 +45,7 @@ export function appearanceMessages(brief) {
42
45
  }
43
46
  export function checkAppearance(text) {
44
47
  const fix = "Use Try again; if it keeps happening, choose another model in the Providers section of Edit project.";
45
- const parsed = appearanceSchema.safeParse(jsonObjectOf(text));
48
+ const parsed = appearanceSchema.safeParse(jsonBetween(text, "{", "}"));
46
49
  if (!parsed.success)
47
50
  return { ok: false, reason: `The AI model didn't answer with the looks it looked up. ${fix}` };
48
51
  const tidy = (figure) => ({
@@ -81,7 +84,7 @@ export function appearanceFor(appearance, scene, options = {}) {
81
84
  // The prompt with its looks; a line asking for looks nobody in the picture has is left out.
82
85
  export function withAppearance(body, text) {
83
86
  if (text !== "")
84
- return render(body, { [appearanceKeyword]: text });
87
+ return render(body, { [appearanceKeyword]: text, Slopify_Appearance: text });
85
88
  return body
86
89
  .split("\n")
87
90
  .filter((line) => !usesAppearance(line))
@@ -93,15 +96,3 @@ function names(text, name) {
93
96
  const escaped = name.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
94
97
  return new RegExp(`(^|[^\\p{L}\\p{N}])${escaped}($|[^\\p{L}\\p{N}])`, "iu").test(text);
95
98
  }
96
- function jsonObjectOf(text) {
97
- const start = text.indexOf("{");
98
- const end = text.lastIndexOf("}");
99
- if (start === -1 || end <= start)
100
- return undefined;
101
- try {
102
- return JSON.parse(text.slice(start, end + 1));
103
- }
104
- catch {
105
- return undefined;
106
- }
107
- }