@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
@@ -11,9 +11,9 @@ import { draftFileRoutes } from "./draft-files.js";
11
11
  import { draftProblem } from "./draft-problem.js";
12
12
  import { onInvalid } from "./problem.js";
13
13
  const idParam = z.object({ id: z.uuid() });
14
- const versionBody = discardDraftInputSchema.unwrap().omit({ id: true });
15
- const saveBody = saveDraftInputSchema.unwrap().omit({ id: true });
16
- const startBody = versionBody.extend({ reviewId: z.uuid() }).strict();
14
+ export const versionBody = discardDraftInputSchema.unwrap().omit({ id: true });
15
+ export const saveBody = saveDraftInputSchema.unwrap().omit({ id: true });
16
+ export const startBody = versionBody.extend({ reviewId: z.uuid() }).strict();
17
17
  const summariesSchema = z
18
18
  .object({
19
19
  drafts: z.array(z
@@ -7,30 +7,27 @@ import { formerNames } from "../../slices/library/rename.js";
7
7
  import { entryById, listEntries } from "../../slices/library/repo.js";
8
8
  import { createEntry, removeEntry, restoreEntry, updateEntry } from "../../slices/library/save.js";
9
9
  import { usedBy } from "../../slices/library/used-by.js";
10
+ import { idParam } from "./ids.js";
10
11
  import { onInvalid } from "./problem.js";
11
12
  // One rule set for prompts and entries, so one refusal mapping too.
12
13
  import { refused, versionParam } from "./prompts.js";
13
- const idParam = z.object({
14
- id: z
15
- .string()
16
- .min(1)
17
- .max(64)
18
- .regex(/^[0-9A-Za-z_-]+$/),
19
- });
20
14
  const entryBody = z.object({
21
15
  category: z.enum(entryCategories),
22
16
  mode: z.enum(entryModes),
23
17
  name: z.string(),
24
18
  body: z.string(),
25
19
  });
20
+ // Duplicate names the entry it copies, whose old keyword names the copy may keep.
21
+ const entryCreateBody = entryBody.extend({ copyOf: z.string().min(1).max(100).optional() });
26
22
  // The return type is inferred so Hono keeps the route types the SPA's client is
27
23
  // generated from; see stagingRoutes.
28
24
  export function entryRoutes(deps) {
29
25
  const library = { db: deps.db, ids: deps.ids, clock: deps.clock };
30
26
  return new Hono()
31
27
  .get("/", (c) => c.json({ entries: listEntries(deps.db) }))
32
- .post("/", zValidator("json", entryBody, onInvalid), (c) => {
33
- const result = createEntry(library, c.req.valid("json"));
28
+ .post("/", zValidator("json", entryCreateBody, onInvalid), (c) => {
29
+ const { copyOf, ...draft } = c.req.valid("json");
30
+ const result = createEntry(library, draft, copyOf);
34
31
  return result.ok ? c.json(result.value, 201) : refused(c, result, "entry");
35
32
  })
36
33
  .put("/:id", zValidator("param", idParam, onInvalid), zValidator("json", entryBody, onInvalid), (c) => {
@@ -0,0 +1,27 @@
1
+ import { bearerToken, isExtensionOrigin, studioPairing, studioRequestAllowed, } from "../../slices/studio/settings.js";
2
+ import { problem } from "./problem.js";
3
+ // The paired Slopify Studio extension's access to the `/ext` routes: every request carries the
4
+ // pairing token, and the CORS headers name the paired extension's origin, never a web page's
5
+ // and never `*`.
6
+ export function extensionAuth(db) {
7
+ const allowOrigin = (c, pairing) => {
8
+ const origin = c.req.header("origin");
9
+ const paired = studioPairing(db).origin;
10
+ c.header("Vary", "Origin");
11
+ if (!isExtensionOrigin(origin))
12
+ return;
13
+ if (origin === paired || pairing) {
14
+ c.header("Access-Control-Allow-Origin", origin);
15
+ c.header("Access-Control-Allow-Headers", "authorization, content-type");
16
+ c.header("Access-Control-Allow-Methods", "GET, POST, OPTIONS");
17
+ c.header("Access-Control-Max-Age", "600");
18
+ }
19
+ };
20
+ const allowed = (c) => studioRequestAllowed(db, bearerToken(c.req.header("authorization")), c.req.header("origin"));
21
+ const refused = (c) => problem(c, {
22
+ status: 401,
23
+ title: "Unauthorized",
24
+ detail: "The Slopify Studio extension isn't paired with this Slopify. Copy the pairing token from Slopify's Settings → YouTube Studio into the extension's options and press Pair.",
25
+ });
26
+ return { allowOrigin, allowed, refused };
27
+ }
@@ -7,12 +7,9 @@ import { closedReviewSentences } from "../../slices/reviews/external-model.js";
7
7
  import { answerExternalReview } from "../../slices/reviews/external-repo.js";
8
8
  import { verdictAnswerSchema, verdictIssue } from "../../slices/reviews/verdict.js";
9
9
  import { actorOf } from "./actor.js";
10
- import { onInvalid, problem, problemFromError, titleOf } from "./problem.js";
11
- const id = z
12
- .string()
13
- .min(1)
14
- .max(64)
15
- .regex(/^[0-9A-Za-z_-]+$/);
10
+ import { idString } from "./ids.js";
11
+ import { onInvalid, problem, problemFromError } from "./problem.js";
12
+ const id = idString;
16
13
  const reviewParam = z.object({ id, reviewId: id });
17
14
  // A verdict that wasn't saved, what was wrong with it, and the tool and body to send instead.
18
15
  // Said before the review is looked up, so it claims nothing about the review: a fixed verdict
@@ -20,7 +17,6 @@ const reviewParam = z.object({ id, reviewId: id });
20
17
  function notSaved(c, why) {
21
18
  return problem(c, {
22
19
  status: 400,
23
- title: titleOf(400),
24
20
  detail: `The verdict wasn't saved${why}. Call submit_review_verdict again with body {verdict: "pass" | "fail", reasons: ["one short sentence per problem"]} and that fixed; a pass may send reasons: [].`,
25
21
  extensions: { reason: "invalid-verdict" },
26
22
  });
@@ -30,7 +26,6 @@ function notSaved(c, why) {
30
26
  function noVerdict(c) {
31
27
  return problem(c, {
32
28
  status: 400,
33
- title: titleOf(400),
34
29
  detail: 'The verdict wasn\'t saved: no verdict came with it. Call submit_review_verdict again with body {verdict: "pass" | "fail", reasons: ["one short sentence per problem"]}; a pass may send reasons: [].',
35
30
  extensions: { reason: "invalid-verdict" },
36
31
  });
@@ -59,24 +54,30 @@ export function externalReviewRoutes(deps) {
59
54
  .post("/:id/external-reviews/:reviewId/verdict", zValidator("param", reviewParam, onInvalid), zValidator("json", verdictAnswerSchema, verdictRefused), async (c) => {
60
55
  const { id: projectId, reviewId } = c.req.valid("param");
61
56
  const answer = c.req.valid("json");
57
+ const by = actorOf(c);
58
+ // The in-app assistant has no review tool; this holds even if one were called.
59
+ if (by === "assistant")
60
+ return problem(c, {
61
+ status: 403,
62
+ detail: "The assistant can't answer this review: it waits for the agent you connected in Settings → Connections → Agents (MCP). Answer it from that agent, or it passes on its own 2 hours after it began waiting.",
63
+ extensions: { reason: "assistant" },
64
+ });
62
65
  const result = answerExternalReview(deps.db, {
63
66
  projectId,
64
67
  id: reviewId,
65
68
  verdict: { passed: answer.verdict === "pass", reasons: answer.reasons },
66
- by: actorOf(c),
69
+ by,
67
70
  at: deps.clock.now().toISOString(),
68
71
  });
69
72
  if (!result.ok)
70
73
  return result.reason === "not-found"
71
74
  ? problem(c, {
72
75
  status: 404,
73
- title: titleOf(404),
74
76
  detail: "This review doesn't exist: the project may have been deleted. Call needs_attention to see what is waiting now.",
75
77
  extensions: { reason: "not-found" },
76
78
  })
77
79
  : problem(c, {
78
80
  status: 409,
79
- title: titleOf(409),
80
81
  detail: `The verdict wasn't saved. ${closedReviewSentences[result.reason]}`,
81
82
  extensions: { reason: result.reason },
82
83
  });
@@ -3,19 +3,13 @@ import { Hono } from "hono";
3
3
  import { z } from "zod";
4
4
  import { findDownload, imagesZip } from "../../slices/storage/downloads.js";
5
5
  import { fileResponse } from "./byte-range.js";
6
- import { onInvalid, problem, titleOf } from "./problem.js";
6
+ import { projectIdParam } from "./ids.js";
7
+ import { onInvalid, problem } from "./problem.js";
7
8
  import { waveformAnswer } from "./waveform.js";
8
- const projectParam = z.object({
9
- projectId: z
10
- .string()
11
- .min(1)
12
- .max(64)
13
- .regex(/^[0-9A-Za-z_-]+$/),
14
- });
15
- const setParam = projectParam.extend({
9
+ const setParam = projectIdParam.extend({
16
10
  set: z.enum(["images.zip", "thumbnails.zip", "shorts.zip"]),
17
11
  });
18
- const assetParam = projectParam.extend({
12
+ const assetParam = projectIdParam.extend({
19
13
  asset: z
20
14
  .string()
21
15
  .min(1)
@@ -79,5 +73,5 @@ function missing(c, reason) {
79
73
  "missing-file": "This file was deleted or moved from the project folder. Use More → make it again in its section on the project page to make it again.",
80
74
  "no-images": "This project has no images or thumbnail yet. Wait for the Images step to finish, then try again.",
81
75
  };
82
- return problem(c, { status: 404, title: titleOf(404), detail: details[reason] });
76
+ return problem(c, { status: 404, detail: details[reason] });
83
77
  }
@@ -1,7 +1,7 @@
1
1
  import { lstat } from "node:fs/promises";
2
2
  import { dirname, isAbsolute, join, relative, sep } from "node:path";
3
3
  import { folderReplySchema } from "./folder-location-schema.js";
4
- import { problem, titleOf } from "./problem.js";
4
+ import { problem } from "./problem.js";
5
5
  export async function replyForFolder(c, deps, projectId, file) {
6
6
  c.header("Cache-Control", "no-store");
7
7
  if (deps.folderLocation?.container) {
@@ -9,7 +9,6 @@ export async function replyForFolder(c, deps, projectId, file) {
9
9
  if (!host)
10
10
  return problem(c, {
11
11
  status: 503,
12
- title: titleOf(503),
13
12
  detail: "Slopify cannot show where this file is on your computer because its Docker project folder was not set up. You can still use the download buttons, or run the Slopify Docker launcher again to set up the folder.",
14
13
  });
15
14
  try {
@@ -45,7 +44,6 @@ export async function replyForFolder(c, deps, projectId, file) {
45
44
  });
46
45
  return problem(c, {
47
46
  status: 404,
48
- title: titleOf(404),
49
47
  detail: "This output's folder was deleted or moved. Use More → make it again in its section on the project page to make the file again.",
50
48
  });
51
49
  }
@@ -63,7 +61,6 @@ export async function replyForFolder(c, deps, projectId, file) {
63
61
  });
64
62
  return problem(c, {
65
63
  status: 503,
66
- title: titleOf(503),
67
64
  detail: "Slopify could not open a file manager window on the computer it runs on. Make sure you are signed in to that computer's desktop, or use the download buttons instead.",
68
65
  });
69
66
  }
@@ -16,14 +16,12 @@ export function fontsRoutes(deps) {
16
16
  if (contentType === undefined || !contentType.toLowerCase().startsWith("multipart/form-data"))
17
17
  return problem(c, {
18
18
  status: 415,
19
- title: titleOf(415),
20
19
  detail: "The font was not sent as a file. Reload the page and choose a .ttf or .otf file again.",
21
20
  });
22
21
  const body = c.req.raw.body;
23
22
  if (body === null)
24
23
  return problem(c, {
25
24
  status: 400,
26
- title: titleOf(400),
27
25
  detail: "No font file arrived. Choose a .ttf or .otf file and try again.",
28
26
  });
29
27
  const part = await readUpload(contentType, body);
@@ -46,7 +44,6 @@ export function fontsRoutes(deps) {
46
44
  if (!result.success)
47
45
  return problem(c, {
48
46
  status: 404,
49
- title: titleOf(404),
50
47
  detail: "This font no longer exists; it may have been deleted. Reload the page to see your current fonts.",
51
48
  });
52
49
  return undefined;
@@ -64,7 +61,6 @@ export function fontsRoutes(deps) {
64
61
  throw error;
65
62
  return problem(c, {
66
63
  status: 404,
67
- title: titleOf(404),
68
64
  detail: "This font no longer exists; it may have been deleted. Reload the page to see your current fonts.",
69
65
  });
70
66
  }
@@ -140,19 +136,16 @@ function failure(reason) {
140
136
  case "unsupported-format":
141
137
  return {
142
138
  status: 415,
143
- title: titleOf(415),
144
139
  detail: "Slopify only accepts .ttf or .otf font files. Choose a file with one of those endings.",
145
140
  };
146
141
  case "unsafe-filename":
147
142
  return {
148
143
  status: 400,
149
- title: titleOf(400),
150
144
  detail: "The font's file name contains characters Slopify cannot use. Rename the file on your computer, then upload it again.",
151
145
  };
152
146
  case "invalid-font":
153
147
  return {
154
148
  status: 400,
155
- title: titleOf(400),
156
149
  detail: "This file is not a valid TrueType (.ttf) or OpenType (.otf) font; it may be damaged. Download the font again or choose another.",
157
150
  };
158
151
  }
@@ -1,28 +1,55 @@
1
1
  import { zValidator } from "@hono/zod-validator";
2
2
  import { Hono } from "hono";
3
3
  import { z } from "zod";
4
+ import { snoozeDays } from "../../slices/autopilot/model.js";
5
+ import { setWeeklyNotify, snoozeWarning, weeklyNotifyOf } from "../../slices/autopilot/repo.js";
6
+ import { autopilotView } from "../../slices/autopilot/signals.js";
4
7
  import { weekSummary } from "../../slices/run-cost/week.js";
8
+ import { notReadySentence, runnableTemplate } from "../../slices/template-studio/schedule-gate.js";
5
9
  import { markUploaded, setAside } from "../../slices/uploads/repo.js";
6
- import { onInvalid, problem, titleOf } from "./problem.js";
10
+ import { idParam, idString } from "./ids.js";
11
+ import { onInvalid, problem } from "./problem.js";
7
12
  const weekQuery = z.object({
8
13
  since: z.iso.datetime({ offset: true }),
9
- channelId: z.string().min(1).max(64).optional(),
14
+ channelId: idString.optional(),
10
15
  });
11
16
  // Home's "This week": videos made, spend and the API equivalent since `since` (the browser's
12
17
  // own Monday), for one channel or all of them, and where each CLI plan's windows stand.
13
18
  export function homeRoutes(deps) {
14
- return new Hono().get("/week", zValidator("query", weekQuery, onInvalid), (c) => {
19
+ const catalogue = deps.catalogue;
20
+ return (new Hono()
21
+ .get("/week", zValidator("query", weekQuery, onInvalid), (c) => {
15
22
  const { since, channelId } = c.req.valid("query");
16
23
  return c.json(weekSummary(deps.db, new Date(since).toISOString(), channelId));
17
- });
24
+ })
25
+ // How far ahead the content is and what may stop the schedules, from the database now
26
+ // and the hourly check's last answers: it never asks a CLI or waits for one.
27
+ .get("/autopilot", zValidator("query", channelQuery, onInvalid), (c) => c.json(autopilotView({
28
+ db: deps.db,
29
+ clock: deps.clock,
30
+ // What the schedules actually run (`template-studio/schedule-gate.ts`).
31
+ template: (id) => runnableTemplate(deps.db, id),
32
+ notReady: (id) => notReadySentence(deps.db, id),
33
+ ...(catalogue === undefined ? {} : { catalogue: () => catalogue.read() }),
34
+ }, deps.autopilot?.latest(), c.req.valid("query").channelId)))
35
+ // Hide: the warning stays hidden for a week, or until it changes.
36
+ .post("/autopilot/snooze", zValidator("json", snoozeBody, onInvalid), (c) => {
37
+ const { key } = c.req.valid("json");
38
+ const until = new Date(deps.clock.now().valueOf() + snoozeDays * 24 * 60 * 60_000);
39
+ snoozeWarning(deps.db, key, until);
40
+ return c.json({ key, until: until.toISOString() });
41
+ })
42
+ // Settings → Notifications' Weekly report.
43
+ .get("/autopilot/weekly", (c) => c.json({ enabled: weeklyNotifyOf(deps.db) }))
44
+ .put("/autopilot/weekly", zValidator("json", weeklyBody, onInvalid), (c) => {
45
+ const { enabled } = c.req.valid("json");
46
+ setWeeklyNotify(deps.db, enabled);
47
+ return c.json({ enabled });
48
+ }));
18
49
  }
19
- const idParam = z.object({
20
- id: z
21
- .string()
22
- .min(1)
23
- .max(64)
24
- .regex(/^[0-9A-Za-z_-]+$/),
25
- });
50
+ const channelQuery = z.object({ channelId: idString.optional() });
51
+ const snoozeBody = z.object({ key: z.string().min(1).max(300) }).strict();
52
+ const weeklyBody = z.object({ enabled: z.boolean() }).strict();
26
53
  // Home's "Mark uploaded" and its undo. Slopify never uploads, so this only records what the
27
54
  // person says they did.
28
55
  export function uploadedRoutes(deps) {
@@ -33,7 +60,6 @@ export function uploadedRoutes(deps) {
33
60
  if (!result.ok)
34
61
  return problem(c, {
35
62
  status: 404,
36
- title: titleOf(404),
37
63
  detail: "This project no longer exists. Go back to Projects to pick another.",
38
64
  });
39
65
  return c.json({ setAside: c.req.valid("json").setAside });
@@ -43,7 +69,6 @@ export function uploadedRoutes(deps) {
43
69
  if (!result.ok)
44
70
  return problem(c, {
45
71
  status: 404,
46
- title: titleOf(404),
47
72
  detail: "This project no longer exists. Go back to Projects to pick another.",
48
73
  });
49
74
  return c.json({ uploadedAt: result.uploadedAt });
@@ -4,6 +4,7 @@ import { bodyLimit } from "hono/body-limit";
4
4
  import { stream } from "hono/streaming";
5
5
  import { z } from "zod";
6
6
  import { sniffImage } from "../../adapters/image/bytes.js";
7
+ import { internalError } from "../../kernel/errors.js";
7
8
  import { redact } from "../../kernel/log.js";
8
9
  import { bridgeLimits, encodeHostImageReport, HostFolderRefused, hostCliIds, hostCliProtocol, hostDoneFrame, hostFrameSchema, hostFramesHeader, hostImageReportHeader, hostImageSchema, hostLlmIds, hostLlmSchema, hostModelsSchema, hostOpenFolderSchema, hostStatusSchema, } from "../../kernel/ports/host-cli.js";
9
10
  import { agentImageTimeoutMs } from "../../kernel/ports/image.js";
@@ -137,7 +138,7 @@ export function hostCliRoutes(options) {
137
138
  onError: (c) => c.json({
138
139
  type: "error",
139
140
  kind: "unavailable",
140
- message: "Slopify hit an internal error (a request to the helper on your computer was too large). Try again; if it keeps happening, use Download diagnostics in Settings and report it.",
141
+ message: internalError("a request to the helper on your computer was too large"),
141
142
  }, 413),
142
143
  }));
143
144
  app.get("/v1/status/:provider", async (c) => {
@@ -149,7 +150,7 @@ export function hostCliRoutes(options) {
149
150
  if (Buffer.byteLength(text) > bridgeLimits.status)
150
151
  throw providerError({
151
152
  kind: "unavailable",
152
- message: "Slopify hit an internal error (the helper on your computer sent a status report that was too large). Try again; if it keeps happening, use Download diagnostics in Settings and report it.",
153
+ message: internalError("the helper on your computer sent a status report that was too large"),
153
154
  });
154
155
  return c.json(status);
155
156
  });
@@ -163,7 +164,7 @@ export function hostCliRoutes(options) {
163
164
  if (Buffer.byteLength(JSON.stringify(result)) > bridgeLimits.models)
164
165
  throw providerError({
165
166
  kind: "unavailable",
166
- message: "Slopify hit an internal error (the helper on your computer sent a model list that was too large). Try again; if it keeps happening, use Download diagnostics in Settings and report it.",
167
+ message: internalError("the helper on your computer sent a model list that was too large"),
167
168
  });
168
169
  return c.json(result);
169
170
  });
@@ -202,11 +203,11 @@ export function hostCliRoutes(options) {
202
203
  ? { type: "activity" }
203
204
  : event);
204
205
  if (done || parsed.type === "error")
205
- throw new Error("Slopify hit an internal error (the AI tool on your computer sent a reply Slopify could not read). Try again; if it keeps happening, use Download diagnostics in Settings and report it.");
206
+ throw new Error(internalError("the AI tool on your computer sent a reply Slopify could not read"));
206
207
  const frame = `${JSON.stringify(parsed)}\n`;
207
208
  total += Buffer.byteLength(frame);
208
209
  if (Buffer.byteLength(frame) > bridgeLimits.frame || total > bridgeLimits.stream)
209
- throw new Error("Slopify hit an internal error (the AI tool on your computer sent a reply that was too large). Try again; if it keeps happening, use Download diagnostics in Settings and report it.");
210
+ throw new Error(internalError("the AI tool on your computer sent a reply that was too large"));
210
211
  await output.write(frame);
211
212
  signal.throwIfAborted();
212
213
  done = parsed.type === "done";
@@ -238,7 +239,7 @@ export function hostCliRoutes(options) {
238
239
  const signal = AbortSignal.any([job.signal, controller.signal]);
239
240
  const timer = setTimeout(() => controller.abort(), agentImageTimeoutMs);
240
241
  try {
241
- const { reference, thinking, cast: bridgedCast, ...rest } = body.data;
242
+ const { reference, thinking, cast: bridgedCast, edit: bridgedEdit, ...rest } = body.data;
242
243
  const cast = bridgedCast?.map((member) => ({
243
244
  name: member.name,
244
245
  description: member.description,
@@ -257,17 +258,26 @@ export function hostCliRoutes(options) {
257
258
  };
258
259
  if (image !== undefined && sniffImage(image.bytes) !== image.mime)
259
260
  return invalid(c);
261
+ const source = bridgedEdit === undefined
262
+ ? undefined
263
+ : {
264
+ bytes: new Uint8Array(Buffer.from(bridgedEdit.source.base64, "base64")),
265
+ mime: bridgedEdit.source.mime,
266
+ };
267
+ if (source !== undefined && sniffImage(source.bytes) !== source.mime)
268
+ return invalid(c);
260
269
  const result = await ports.image.generate({
261
270
  ...rest,
262
271
  ...(thinking === undefined ? {} : { thinking }),
263
272
  ...(image === undefined ? {} : { reference: image }),
264
273
  ...(cast === undefined ? {} : { cast }),
274
+ ...(source === undefined ? {} : { edit: { source } }),
265
275
  signal,
266
276
  });
267
277
  if (signal.aborted)
268
278
  throw providerError({ kind: "unavailable", message: unavailable });
269
279
  if (result.bytes.byteLength > bridgeLimits.image || sniffImage(result.bytes) !== result.mime)
270
- throw new Error("Slopify hit an internal error (the image made on your computer was too large or not a valid image). Try again; if it keeps happening, use Download diagnostics in Settings and report it.");
280
+ throw new Error(internalError("the image made on your computer was too large or not a valid image"));
271
281
  c.header("Content-Type", result.mime);
272
282
  c.header("Content-Length", String(result.bytes.byteLength));
273
283
  // Any app may get this: one from before it ignores headers it does not know.
@@ -328,6 +338,6 @@ function invalid(c) {
328
338
  return c.json({
329
339
  type: "error",
330
340
  kind: "unavailable",
331
- message: "Slopify hit an internal error (the helper on your computer received a request it could not read). Try again; if it keeps happening, use Download diagnostics in Settings and report it.",
341
+ message: internalError("the helper on your computer received a request it could not read"),
332
342
  }, 400);
333
343
  }
@@ -0,0 +1,8 @@
1
+ import { z } from "zod";
2
+ export const idString = z
3
+ .string()
4
+ .min(1)
5
+ .max(64)
6
+ .regex(/^[0-9A-Za-z_-]+$/);
7
+ export const idParam = z.object({ id: idString });
8
+ export const projectIdParam = z.object({ projectId: idString });
@@ -0,0 +1,35 @@
1
+ import { zValidator } from "@hono/zod-validator";
2
+ import { Hono } from "hono";
3
+ import { projectById, projectTrashed } from "../../slices/admission/repo.js";
4
+ import { projectChannelId } from "../../slices/channels/repo.js";
5
+ import { keywordFactsFor } from "../../slices/keywords/facts.js";
6
+ import { costLabel, placeLabels } from "../../slices/keywords/model.js";
7
+ import { keywordSpecs } from "../../slices/keywords/specs.js";
8
+ import { projectIdParam } from "./ids.js";
9
+ import { onInvalid, problem } from "./problem.js";
10
+ // The keywords Slopify fills, as the editor's Insert bar and agents read them, and a project's
11
+ // facts copied again for Edit project's Refresh keyword facts.
12
+ export function keywordRoutes(deps) {
13
+ return new Hono()
14
+ .get("/", (c) => c.json({
15
+ keywords: keywordSpecs.map((spec) => ({
16
+ ...spec,
17
+ cost: { kind: spec.cost, label: costLabel(spec.cost) },
18
+ places: spec.places.map((place) => ({ place, label: placeLabels[place] })),
19
+ })),
20
+ }))
21
+ .get("/facts/:projectId", zValidator("param", projectIdParam, onInvalid), (c) => {
22
+ const { projectId } = c.req.valid("param");
23
+ const project = projectById(deps.db, projectId);
24
+ if (project === undefined)
25
+ return problem(c, {
26
+ status: 404,
27
+ detail: projectTrashed(deps.db, projectId)
28
+ ? "This project is in the trash. Restore it in Settings → Trash, then refresh its keyword facts."
29
+ : "This project no longer exists. Go back to Projects to pick another.",
30
+ });
31
+ return c.json({
32
+ keywordFacts: keywordFactsFor(deps, { ...project.config, channelId: projectChannelId(deps.db, projectId) }, deps.clock.now(), projectId),
33
+ });
34
+ });
35
+ }
@@ -0,0 +1,132 @@
1
+ import { zValidator } from "@hono/zod-validator";
2
+ import { Hono } from "hono";
3
+ import { z } from "zod";
4
+ import { errorText } from "../../kernel/errors.js";
5
+ import { providerFamilies } from "../../kernel/ports/model.js";
6
+ import { autoModes } from "../../slices/model-checks/model.js";
7
+ import { accountOf, markNoticesSeen, writeAuto } from "../../slices/model-checks/repo.js";
8
+ import { dollarsText } from "../../slices/model-checks/rules.js";
9
+ import { providerById, providerIds } from "../../slices/settings/model.js";
10
+ import { onInvalid, problem } from "./problem.js";
11
+ const providerParam = z.object({ id: z.enum(providerIds) });
12
+ const modelList = z.string().min(1).max(200);
13
+ const estimateQuery = z.object({ models: z.string().max(10_000).optional() });
14
+ export const checkBody = z
15
+ .object({
16
+ models: z.array(modelList).max(300).optional(),
17
+ confirmedDollars: z.number().finite().nonnegative().optional(),
18
+ })
19
+ .strict();
20
+ const dismissBody = z
21
+ .object({
22
+ keys: z
23
+ .array(z
24
+ .object({
25
+ provider: z.string().min(1).max(64),
26
+ family: z.enum(providerFamilies),
27
+ model: modelList,
28
+ })
29
+ .strict())
30
+ .max(500),
31
+ })
32
+ .strict();
33
+ const autoBody = z.object({ auto: z.enum(autoModes) }).strict();
34
+ const notReady = "Model checks are not ready yet because Slopify is still starting. Wait a moment, reload the page and try again.";
35
+ export function estimateOf(plan) {
36
+ return {
37
+ count: plan.targets.length,
38
+ totalDollars: plan.totalDollars,
39
+ unpriced: plan.unpriced,
40
+ planAccounts: plan.planAccounts,
41
+ text: dollarsText(plan.totalDollars, plan.unpriced),
42
+ skipped: plan.skipped.map((one) => ({ model: one.model.model, reason: one.reason })),
43
+ };
44
+ }
45
+ function unavailable(c) {
46
+ return problem(c, { status: 503, detail: notReady });
47
+ }
48
+ function noAccount(c, id) {
49
+ const { displayName, auth } = providerById(id);
50
+ return problem(c, {
51
+ status: 400,
52
+ detail: auth === "key"
53
+ ? `No ${displayName} key is saved, so its models can't be checked. Paste a key in Settings → Providers → ${displayName} and choose Save, then choose Check models in Settings → Models.`
54
+ : `${displayName} is not set up, so its models can't be checked. Set it up in Settings → Providers, then choose Check models in Settings → Models.`,
55
+ });
56
+ }
57
+ // The provider-scoped half: its estimate and its Check models button.
58
+ export function providerCheckRoutes(deps) {
59
+ const checks = () => deps.modelChecks;
60
+ const models = (raw) => raw === undefined || raw.trim() === ""
61
+ ? undefined
62
+ : raw
63
+ .split(",")
64
+ .map((one) => one.trim())
65
+ .filter(Boolean);
66
+ return new Hono()
67
+ .get("/:id/models/check/estimate", zValidator("param", providerParam, onInvalid), zValidator("query", estimateQuery, onInvalid), async (c) => {
68
+ const service = checks();
69
+ if (service === undefined)
70
+ return unavailable(c);
71
+ const { id } = c.req.valid("param");
72
+ if (accountOf(deps.db, id) === undefined)
73
+ return noAccount(c, id);
74
+ c.header("Cache-Control", "no-store");
75
+ const plan = await service.plan({
76
+ trigger: "manual",
77
+ provider: id,
78
+ models: models(c.req.valid("query").models),
79
+ });
80
+ return c.json({ provider: id, ...estimateOf(plan) });
81
+ })
82
+ .post("/:id/models/check", zValidator("param", providerParam, onInvalid), zValidator("json", checkBody, onInvalid), async (c) => {
83
+ const service = checks();
84
+ if (service === undefined)
85
+ return unavailable(c);
86
+ const { id } = c.req.valid("param");
87
+ if (accountOf(deps.db, id) === undefined)
88
+ return noAccount(c, id);
89
+ const body = c.req.valid("json");
90
+ const name = providerById(id).displayName;
91
+ const answer = await service.start({ trigger: "manual", provider: id, models: body.models }, body.confirmedDollars);
92
+ if (!answer.ok && answer.reason === "running")
93
+ return problem(c, {
94
+ status: 409,
95
+ detail: `Slopify is already checking ${name}'s models. Wait for it to finish, then choose Check models again in Settings → Models.`,
96
+ });
97
+ if (!answer.ok) {
98
+ const estimate = estimateOf(answer.plan);
99
+ return problem(c, {
100
+ status: 409,
101
+ detail: `Checking ${String(estimate.count)} ${name} model${estimate.count === 1 ? "" : "s"} costs ${estimate.text} at list price, and nothing was sent yet. Choose Check models in Settings → Models again and confirm the estimate to go ahead.`,
102
+ extensions: { estimate },
103
+ });
104
+ }
105
+ void answer.done.catch((error) => {
106
+ deps.log.write("warn", "model-checks", {
107
+ detail: `${id}: ${errorText(error)}`,
108
+ });
109
+ });
110
+ return c.json({ provider: id, started: true, ...estimateOf(answer.plan) }, 202);
111
+ });
112
+ }
113
+ // Settings → Models: what each provider's checks found, New for you, and Automatic checks.
114
+ export function modelCheckRoutes(deps) {
115
+ return new Hono()
116
+ .get("/", (c) => {
117
+ const service = deps.modelChecks;
118
+ if (service === undefined)
119
+ return unavailable(c);
120
+ c.header("Cache-Control", "no-store");
121
+ return c.json(service.overview());
122
+ })
123
+ .post("/notices/dismiss", zValidator("json", dismissBody, onInvalid), (c) => {
124
+ markNoticesSeen(deps.db, c.req.valid("json").keys);
125
+ return c.body(null, 204);
126
+ })
127
+ .put("/auto", zValidator("json", autoBody, onInvalid), (c) => {
128
+ const { auto } = c.req.valid("json");
129
+ writeAuto(deps.db, auto);
130
+ return c.json({ auto });
131
+ });
132
+ }