@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
@@ -0,0 +1,80 @@
1
+ // The address of what connected to this server: the browser, or a proxy in front of it.
2
+ // Undefined for an in-process request, which has no socket.
3
+ export function peerAddress(c) {
4
+ const env = c.env;
5
+ if (typeof env !== "object" || env === null || !("incoming" in env))
6
+ return undefined;
7
+ const incoming = env.incoming;
8
+ if (typeof incoming !== "object" || incoming === null || !("socket" in incoming))
9
+ return undefined;
10
+ const socket = incoming.socket;
11
+ if (typeof socket !== "object" || socket === null || !("remoteAddress" in socket))
12
+ return undefined;
13
+ return typeof socket.remoteAddress === "string" ? socket.remoteAddress : undefined;
14
+ }
15
+ const loopbackPeer = (address) => address !== undefined &&
16
+ (address === "::1" || /^(?:::ffff:)?127\.\d{1,3}\.\d{1,3}\.\d{1,3}$/u.test(address));
17
+ // The name the browser used, as a proxy on this computer passes it on in X-Forwarded-Host
18
+ // (its first value). Only trusted from a loopback peer: anyone else could write the header.
19
+ export function forwardedHost(c) {
20
+ if (!loopbackPeer(peerAddress(c)))
21
+ return undefined;
22
+ const first = c.req.header("x-forwarded-host")?.split(",")[0]?.trim();
23
+ return first === undefined || first === "" ? undefined : first;
24
+ }
25
+ // host[:port] with the scheme's default port left out, as an Origin's own host has it.
26
+ function hostOf(host, scheme) {
27
+ const lower = host.trim().toLowerCase();
28
+ const fallback = scheme === "https:" ? ":443" : scheme === "http:" ? ":80" : undefined;
29
+ return fallback !== undefined && lower.endsWith(fallback)
30
+ ? lower.slice(0, -fallback.length)
31
+ : lower;
32
+ }
33
+ // Standard same-origin protection: a browser's Origin names the page that sent the request,
34
+ // and a page at the very host:port the browser addressed is Slopify's own, whatever proxy
35
+ // (Slopify's remote access, a `tailscale serve` set up by hand, Caddy, nginx) stands in front.
36
+ // A cross-site page names its own host, so it never matches; which names may reach Slopify at
37
+ // all is `request-guard.ts`'s Host check.
38
+ function sameHost(c, origin) {
39
+ let url;
40
+ try {
41
+ url = new URL(origin);
42
+ }
43
+ catch {
44
+ return false;
45
+ }
46
+ if (url.host === "")
47
+ return false;
48
+ const own = url.host.toLowerCase();
49
+ return [c.req.header("host"), forwardedHost(c)].some((host) => host !== undefined && hostOf(host, url.protocol) === own);
50
+ }
51
+ // Whether a request with an Origin comes from the Slopify page itself: the page this server
52
+ // serves, the page at the address the browser used for this request (through any proxy), or
53
+ // the same page at the exact https address Slopify set up in Tailscale.
54
+ export function fromSlopifyPage(c, remoteHost) {
55
+ const origin = c.req.header("origin");
56
+ if (origin === undefined || origin === new URL(c.req.url).origin)
57
+ return true;
58
+ if (sameHost(c, origin))
59
+ return true;
60
+ const remote = remoteHost?.();
61
+ return (remote !== undefined &&
62
+ origin === `https://${remote}` &&
63
+ (c.req.header("x-forwarded-host") ?? c.req.header("host")) === remote);
64
+ }
65
+ // A request that reached Slopify through a forwarding proxy, Tailscale serve included: it sets
66
+ // X-Forwarded-For on every request and keeps the ts.net Host. Agents' endpoints answer only
67
+ // requests made on this computer.
68
+ export function viaProxy(headers) {
69
+ if (headers.has("x-forwarded-for") || headers.has("x-forwarded-host"))
70
+ return true;
71
+ if (headers.has("tailscale-user-login") || headers.has("tailscale-funnel-request"))
72
+ return true;
73
+ const host = headers.get("host") ?? "";
74
+ const name = host.replace(/:\d+$/u, "").toLowerCase();
75
+ return name.endsWith(".ts.net");
76
+ }
77
+ // Tailscale marks every request that came through Funnel, from the public internet.
78
+ export function viaFunnel(headers) {
79
+ return headers.has("tailscale-funnel-request");
80
+ }
@@ -5,15 +5,22 @@ import { z } from "zod";
5
5
  import { calendarRange } from "../../slices/schedules/agenda.js";
6
6
  import { rejectHeldTopics, restoreRejectedTopics } from "../../slices/schedules/held-bulk.js";
7
7
  import { preparedTopics, prepareTopic } from "../../slices/schedules/prepare.js";
8
- import { calendarMaxDays, calendarSchema, heldTopicEditSchema, heldTopicSchema, queueMax, scheduleCreateSchema, scheduleRunSchema, scheduleSummarySchema, scheduleUpdateSchema, topicMoveSchema, topicQueueSchema, topicTransferSchema, } from "../../slices/schedules/schema.js";
8
+ import { rescoreTopics } from "../../slices/schedules/rescore.js";
9
+ import { calendarMaxDays, calendarSchema, heldTopicEditSchema, heldTopicSchema, queueMax, scheduleCreateSchema, scheduleRunSchema, scheduleSaveBodySchema, scheduleSummarySchema, scheduleVersionBodySchema, topicMoveSchema, topicQueueSchema, topicTransferSchema, } from "../../slices/schedules/schema.js";
9
10
  import { cancelSchedule, createSchedule, deleteSchedule, listScheduleRuns, listSchedules, pauseSchedule, resumeSchedule, updateSchedule, } from "../../slices/schedules/service.js";
11
+ import { scoresFor } from "../../slices/schedules/topic-scores.js";
10
12
  import { approveHeldTopics, editHeldTopic, heldTopics, moveTopic, rejectHeldTopic, replaceTopics, transferTopic, } from "../../slices/schedules/topics.js";
11
- import { onInvalid, problem, titleOf } from "./problem.js";
13
+ import { onInvalid, problem } from "./problem.js";
14
+ import { refusal } from "./refusal.js";
12
15
  const id = z.object({ id: z.uuid() });
13
16
  const topicParam = z.object({ id: z.uuid(), topicId: z.string().min(1).max(100) });
14
17
  const topicIds = z.array(z.string().min(1).max(100)).min(1).max(queueMax);
15
- const heldIds = z.object({ ids: topicIds }).strict();
16
- const heldRestore = z
18
+ // Prepare one queued topic: its title as the queue lists it.
19
+ export const prepareTopicBody = z.object({ topic: z.string().min(1).max(500) }).strict();
20
+ // Approve one held topic, optionally under another title.
21
+ export const approveHeldTopicBody = z.object({ title: z.string().optional() }).strict();
22
+ export const heldIds = z.object({ ids: topicIds }).strict();
23
+ export const heldRestore = z
17
24
  .object({
18
25
  topics: z
19
26
  .array(z
@@ -27,18 +34,6 @@ const heldRestore = z
27
34
  })
28
35
  .strict();
29
36
  function refused(c, result) {
30
- const status = ["not-found", "missing-template", "topic-not-found"].includes(result.reason)
31
- ? 404
32
- : [
33
- "conflict",
34
- "cancel-required",
35
- "spend-limit",
36
- "unsupported-media",
37
- "queue-full",
38
- "busy",
39
- ].includes(result.reason)
40
- ? 409
41
- : 400;
42
37
  const detail = {
43
38
  "invalid-input": "Some schedule settings are not valid. Check each field and save again.",
44
39
  "not-found": "This schedule no longer exists; it may have been deleted. Reload the page to see your schedules.",
@@ -52,13 +47,23 @@ function refused(c, result) {
52
47
  "queue-full": `A schedule's topic queue holds at most ${String(queueMax)} topics. Remove some under Schedules → Edit, then try again.`,
53
48
  "topic-not-found": "This topic was already approved, turned down or used. Reload the page to see the topics waiting now.",
54
49
  busy: "Slopify is already generating topics for this schedule. Wait for it to finish, then try again.",
50
+ "not-ready": "This schedule's template has no version marked ready. Open it in Library → Templates → Open studio and press Mark ready.",
55
51
  "invalid-topics": "Some topics name keywords this template does not use. Check each topic under Schedules → Edit and save again.",
56
52
  };
57
- return problem(c, {
58
- status,
59
- title: titleOf(status),
53
+ return refusal(c, result.reason, {
54
+ statuses: {
55
+ "not-found": 404,
56
+ "missing-template": 404,
57
+ "topic-not-found": 404,
58
+ conflict: 409,
59
+ "cancel-required": 409,
60
+ "spend-limit": 409,
61
+ "unsupported-media": 409,
62
+ "queue-full": 409,
63
+ busy: 409,
64
+ },
65
+ otherwise: 400,
60
66
  detail: result.message ?? detail[result.reason],
61
- extensions: { reason: result.reason },
62
67
  });
63
68
  }
64
69
  export function scheduleRoutes(deps) {
@@ -90,38 +95,44 @@ function routes(deps) {
90
95
  ? c.json({
91
96
  schedule: scheduleSummarySchema.parse(schedule),
92
97
  runs: runs.value.map((run) => scheduleRunSchema.parse(run)),
98
+ // Each queued and held topic's score and reasons, by `topicKey`, while ranking
99
+ // is on.
100
+ scores: scoresFor(service().db, schedule.id, [
101
+ ...schedule.items.map((item) => item.title),
102
+ ...heldTopics(service(), schedule.id).map((topic) => topic.title),
103
+ ]),
93
104
  })
94
105
  : refused(c, runs);
95
106
  })
96
- .put("/:id", zValidator("param", id, onInvalid), zValidator("json", scheduleUpdateSchema.unwrap().omit({ id: true }), onInvalid), (c) => {
107
+ .put("/:id", zValidator("param", id, onInvalid), zValidator("json", scheduleSaveBodySchema, onInvalid), (c) => {
97
108
  const result = updateSchedule(service(), {
98
109
  ...c.req.valid("json"),
99
110
  id: c.req.valid("param").id,
100
111
  });
101
112
  return result.ok ? c.json(scheduleSummarySchema.parse(result.value)) : refused(c, result);
102
113
  })
103
- .post("/:id/pause", zValidator("param", id, onInvalid), zValidator("json", z.object({ baseVersion: z.number().int().positive() }).strict(), onInvalid), (c) => {
114
+ .post("/:id/pause", zValidator("param", id, onInvalid), zValidator("json", scheduleVersionBodySchema, onInvalid), (c) => {
104
115
  const result = pauseSchedule(service(), {
105
116
  ...c.req.valid("json"),
106
117
  id: c.req.valid("param").id,
107
118
  });
108
119
  return result.ok ? c.json(scheduleSummarySchema.parse(result.value)) : refused(c, result);
109
120
  })
110
- .post("/:id/resume", zValidator("param", id, onInvalid), zValidator("json", z.object({ baseVersion: z.number().int().positive() }).strict(), onInvalid), (c) => {
121
+ .post("/:id/resume", zValidator("param", id, onInvalid), zValidator("json", scheduleVersionBodySchema, onInvalid), (c) => {
111
122
  const result = resumeSchedule(service(), {
112
123
  ...c.req.valid("json"),
113
124
  id: c.req.valid("param").id,
114
125
  });
115
126
  return result.ok ? c.json(scheduleSummarySchema.parse(result.value)) : refused(c, result);
116
127
  })
117
- .post("/:id/cancel", zValidator("param", id, onInvalid), zValidator("json", z.object({ baseVersion: z.number().int().positive() }).strict(), onInvalid), (c) => {
128
+ .post("/:id/cancel", zValidator("param", id, onInvalid), zValidator("json", scheduleVersionBodySchema, onInvalid), (c) => {
118
129
  const result = cancelSchedule(service(), {
119
130
  ...c.req.valid("json"),
120
131
  id: c.req.valid("param").id,
121
132
  });
122
133
  return result.ok ? c.json(scheduleSummarySchema.parse(result.value)) : refused(c, result);
123
134
  })
124
- .delete("/:id", zValidator("param", id, onInvalid), zValidator("json", z.object({ baseVersion: z.number().int().positive() }).strict(), onInvalid), (c) => {
135
+ .delete("/:id", zValidator("param", id, onInvalid), zValidator("json", scheduleVersionBodySchema, onInvalid), (c) => {
125
136
  const result = deleteSchedule(service(), {
126
137
  ...c.req.valid("json"),
127
138
  id: c.req.valid("param").id,
@@ -132,14 +143,30 @@ function routes(deps) {
132
143
  const scheduleId = c.req.valid("param").id;
133
144
  if (listSchedules(service()).every((item) => item.id !== scheduleId))
134
145
  return refused(c, { ok: false, reason: "not-found" });
146
+ const topics = heldTopics(service(), scheduleId).map((topic) => heldTopicSchema.parse(topic));
135
147
  return c.json({
136
- topics: heldTopics(service(), scheduleId).map((topic) => heldTopicSchema.parse(topic)),
148
+ topics,
149
+ scores: scoresFor(service().db, scheduleId, topics.map((topic) => topic.title)),
137
150
  });
151
+ })
152
+ // Rescore: every queued and held topic scored again, then put in score order. With search
153
+ // ranking it asks Google, DuckDuckGo and Wikipedia, up to 15 seconds.
154
+ .post("/:id/topics/score", zValidator("param", id, onInvalid), async (c) => {
155
+ const result = await rescoreTopics(service(), c.req.valid("param").id);
156
+ if (result.ok)
157
+ return c.json(scheduleSummarySchema.parse(result.value));
158
+ if (result.reason === "scoring-off")
159
+ return problem(c, {
160
+ status: 409,
161
+ detail: result.message,
162
+ extensions: { reason: result.reason },
163
+ });
164
+ return refused(c, result);
138
165
  })
139
166
  // Which queued topics are prepared ahead, and their projects.
140
167
  .get("/:id/prepared", zValidator("param", id, onInvalid), (c) => c.json({ prepared: preparedTopics(service(), c.req.valid("param").id) }))
141
168
  // Prepare one queued topic now: everything but the video, which its scheduled day renders.
142
- .post("/:id/topics/prepare", zValidator("param", id, onInvalid), zValidator("json", z.object({ topic: z.string().min(1).max(500) }).strict(), onInvalid), async (c) => {
169
+ .post("/:id/topics/prepare", zValidator("param", id, onInvalid), zValidator("json", prepareTopicBody, onInvalid), async (c) => {
143
170
  const result = await prepareTopic(service(), {
144
171
  scheduleId: c.req.valid("param").id,
145
172
  title: c.req.valid("json").topic,
@@ -149,13 +176,15 @@ function routes(deps) {
149
176
  const detail = {
150
177
  "not-found": "This topic is no longer in the schedule's queue. Reload the page to see the queue as it is now.",
151
178
  "already-prepared": "This topic is already prepared: its project is in Projects under the same title.",
179
+ paused: "Its schedule is paused, so it makes nothing, ahead or on its day. Resume it under Calendar → Schedules, then press Prepare again.",
152
180
  "missing-template": "This schedule's template was deleted, so there is nothing to prepare from. Choose another template under Edit.",
153
181
  "spend-limit": "Preparing it would go over this schedule's spend limit. Raise the limit under Edit, or prepare it from Play instead.",
182
+ design: "A design schedule makes each piece on its day, so there is nothing to prepare ahead. To have a piece now, open the design template (Library → Designs → Templates) and press Make a piece; to have it sooner on a schedule, give its row an earlier date in the Schedule section.",
154
183
  };
155
184
  return problem(c, {
156
185
  status: result.reason === "not-found" ? 404 : 409,
157
- title: titleOf(result.reason === "not-found" ? 404 : 409),
158
- detail: detail[result.reason] ??
186
+ detail: (result.reason === "not-ready" ? result.detail : undefined) ??
187
+ detail[result.reason] ??
159
188
  (result.detail === undefined
160
189
  ? `This topic couldn't be prepared (${result.reason}). Open Play with the schedule's template to see what the setup needs, then try again.`
161
190
  : `This topic couldn't be prepared: ${result.detail} Fix it in the schedule's template (Library → Templates), then try again.`),
@@ -181,7 +210,6 @@ function routes(deps) {
181
210
  if (refusal !== undefined)
182
211
  return problem(c, {
183
212
  status: 409,
184
- title: titleOf(409),
185
213
  detail: refusal,
186
214
  extensions: { reason: "busy" },
187
215
  });
@@ -206,7 +234,7 @@ function routes(deps) {
206
234
  const result = restoreRejectedTopics(service(), c.req.valid("param").id, c.req.valid("json").topics);
207
235
  return result.ok ? c.json(scheduleSummarySchema.parse(result.value)) : refused(c, result);
208
236
  })
209
- .post("/:id/topics/held/:topicId/approve", zValidator("param", topicParam, onInvalid), zValidator("json", z.object({ title: z.string().optional() }).strict(), onInvalid), (c) => {
237
+ .post("/:id/topics/held/:topicId/approve", zValidator("param", topicParam, onInvalid), zValidator("json", approveHeldTopicBody, onInvalid), (c) => {
210
238
  const { id: scheduleId, topicId } = c.req.valid("param");
211
239
  const { title } = c.req.valid("json");
212
240
  const result = approveHeldTopics(service(), scheduleId, [topicId], title === undefined ? {} : { [topicId]: title });
@@ -257,7 +285,6 @@ export function calendarRoutes(deps) {
257
285
  if (!result.ok)
258
286
  return problem(c, {
259
287
  status: 400,
260
- title: titleOf(400),
261
288
  detail: `Choose a range whose end is after its start and at most ${String(calendarMaxDays)} days long.`,
262
289
  extensions: { reason: result.reason },
263
290
  });
@@ -10,9 +10,10 @@ import { readNotificationUrl, saveNotificationUrl } from "../../slices/notificat
10
10
  import { appearances, providerIds } from "../../slices/settings/model.js";
11
11
  import { readSettings, saveSettings } from "../../slices/settings/playback.js";
12
12
  import { addVoice, removeVoice, setVoiceImitatesRealPerson, setVoiceLanguages, voiceIdMax, voiceNameMax, voices, } from "../../slices/settings/voices.js";
13
+ import { clipEncodeSpeeds, noNvenc, renderPriorities, videoEncoders, } from "../../slices/video/encoding.js";
13
14
  import { readChannelLinks, writeChannelLinks } from "../../slices/youtube/edits-repo.js";
14
15
  import { channelLinkNameMax, channelLinksMax, channelLinksProblem, channelLinkUrlMax, } from "../../slices/youtube/placeholders.js";
15
- import { onInvalid, problem, titleOf } from "./problem.js";
16
+ import { onInvalid, problem } from "./problem.js";
16
17
  const idParam = z.object({ id: z.string().min(1).max(64) });
17
18
  // Shape only. The rules - trimming, emptiness, length, the provider speaking at all -
18
19
  // are the slice's, so one set of messages reaches the form.
@@ -26,7 +27,7 @@ const voiceLanguagesBody = z.object({ languages: z.array(z.string().max(20)).max
26
27
  const voiceRealPersonBody = z.object({ imitatesRealPerson: z.boolean() });
27
28
  // Length and scheme are the slice's rules, so their sentences reach the field; this bound only
28
29
  // keeps a pasted novel off the parser.
29
- const notificationBody = z.object({ url: z.string().max(notificationUrlMax * 2) });
30
+ export const notificationBody = z.object({ url: z.string().max(notificationUrlMax * 2) });
30
31
  // Shape and a generous bound only; `channelLinksProblem` says what is wrong in Settings' words.
31
32
  export const channelLinksBody = z.object({
32
33
  links: z
@@ -45,6 +46,10 @@ const playbackBody = z.object({
45
46
  .optional(),
46
47
  // Absent keeps what is saved, as with loudness.
47
48
  subtitleModel: z.enum(englishSubtitleModels).optional(),
49
+ // Absent keeps what is saved, as with loudness.
50
+ videoEncoder: z.enum(videoEncoders).optional(),
51
+ clipEncodeSpeed: z.enum(clipEncodeSpeeds).optional(),
52
+ renderPriority: z.enum(renderPriorities).optional(),
48
53
  });
49
54
  // The return type is inferred so Hono keeps the route types the SPA's client is
50
55
  // generated from; see stagingRoutes.
@@ -61,16 +66,30 @@ export function settingsRoutes(deps) {
61
66
  ...body,
62
67
  loudness: body.loudness ?? saved.loudness,
63
68
  subtitleModel: body.subtitleModel ?? saved.subtitleModel,
69
+ videoEncoder: body.videoEncoder ?? saved.videoEncoder,
70
+ clipEncodeSpeed: body.clipEncodeSpeed ?? saved.clipEncodeSpeed,
71
+ renderPriority: body.renderPriority ?? saved.renderPriority,
64
72
  });
65
73
  if (!result.ok) {
66
74
  return problem(c, {
67
75
  status: 400,
68
- title: titleOf(400),
69
76
  detail: "These settings cannot be saved yet. Fix the highlighted fields and try again.",
70
77
  extensions: { fields: result.fields },
71
78
  });
72
79
  }
73
80
  return c.json(result.settings);
81
+ })
82
+ // Settings → Production defaults → Video encoder offers the NVIDIA card only where its
83
+ // encoder works; the first ask runs a test encode, later ones answer from memory.
84
+ .get("/encoders", async (c) => {
85
+ const status = deps.nvenc === undefined
86
+ ? { available: false, reason: noNvenc }
87
+ : await deps.nvenc();
88
+ return c.json({
89
+ nvidia: status.available
90
+ ? { available: true }
91
+ : { available: false, reason: status.reason },
92
+ });
74
93
  })
75
94
  // The named links `{{Name}}` placeholders in YouTube descriptions fill from
76
95
  // (`slices/youtube/placeholders.ts`).
@@ -81,7 +100,6 @@ export function settingsRoutes(deps) {
81
100
  if (refused !== undefined)
82
101
  return problem(c, {
83
102
  status: 400,
84
- title: titleOf(400),
85
103
  detail: `The channel links weren't saved: ${refused} Then press Save in Settings → Channel links.`,
86
104
  });
87
105
  writeChannelLinks(deps.db, links);
@@ -93,7 +111,6 @@ export function settingsRoutes(deps) {
93
111
  if (!result.ok) {
94
112
  return problem(c, {
95
113
  status: 400,
96
- title: titleOf(400),
97
114
  detail: `The Notification URL wasn't saved: ${result.message} Fix it in Settings → Notifications → Notification URL, then press Save.`,
98
115
  extensions: { fields: [{ field: "url", message: result.message }] },
99
116
  });
@@ -108,7 +125,6 @@ export function settingsRoutes(deps) {
108
125
  if (invalid !== undefined) {
109
126
  return problem(c, {
110
127
  status: 400,
111
- title: titleOf(400),
112
128
  detail: `The test notification wasn't sent: ${invalid}`,
113
129
  extensions: { fields: [{ field: "url", message: invalid }] },
114
130
  });
@@ -118,7 +134,6 @@ export function settingsRoutes(deps) {
118
134
  deps.log.write("warn", "notification.test", { detail: sendFailureText(result) });
119
135
  return problem(c, {
120
136
  status: 502,
121
- title: titleOf(502),
122
137
  detail: `The test notification wasn't delivered: ${sendFailureText(result)}. Check the address in Settings → Notifications → Notification URL (for ntfy, https://ntfy.sh/your-topic), then press Send test notification again.`,
123
138
  });
124
139
  }
@@ -147,12 +162,10 @@ export function settingsRoutes(deps) {
147
162
  return result.reason === "duplicate-voice-id"
148
163
  ? problem(c, {
149
164
  status: 409,
150
- title: titleOf(409),
151
165
  detail: "This voice ID is already saved for this provider. Use the existing voice, or enter a different voice ID.",
152
166
  })
153
167
  : problem(c, {
154
168
  status: 400,
155
- title: titleOf(400),
156
169
  detail: "This voice cannot be added yet. Fix the highlighted field and try again.",
157
170
  extensions: { fields: [fieldOf(result.reason)] },
158
171
  });
@@ -166,12 +179,10 @@ export function settingsRoutes(deps) {
166
179
  return result.reason === "missing"
167
180
  ? problem(c, {
168
181
  status: 404,
169
- title: titleOf(404),
170
182
  detail: "This voice no longer exists; it may have been deleted. Reload Settings → Voices to see your voices.",
171
183
  })
172
184
  : problem(c, {
173
185
  status: 400,
174
- title: titleOf(400),
175
186
  detail: "The languages weren't saved. Fix the highlighted field and try again.",
176
187
  extensions: { fields: [fieldOf("unknown-language")] },
177
188
  });
@@ -183,7 +194,6 @@ export function settingsRoutes(deps) {
183
194
  ? c.body(null, 204)
184
195
  : problem(c, {
185
196
  status: 404,
186
- title: titleOf(404),
187
197
  detail: "This voice no longer exists; it may have been deleted. Reload Settings → Voices to see your voices.",
188
198
  });
189
199
  })
@@ -191,7 +201,6 @@ export function settingsRoutes(deps) {
191
201
  if (!removeVoice(voiceDeps, c.req.valid("param").id).ok) {
192
202
  return problem(c, {
193
203
  status: 404,
194
- title: titleOf(404),
195
204
  detail: "This voice no longer exists; it may have been deleted already. Reload the page to see your voices.",
196
205
  });
197
206
  }