@gentbajko/slopify 3.25.9 → 4.1.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 (718) hide show
  1. package/dist/adapters/account-limits.js +30 -0
  2. package/dist/adapters/alignment/audio.js +1 -1
  3. package/dist/adapters/alignment/cache.js +4 -6
  4. package/dist/adapters/alignment/ctc.js +1 -2
  5. package/dist/adapters/alignment/lock.js +4 -3
  6. package/dist/adapters/alignment/multilingual.js +2 -4
  7. package/dist/adapters/alignment/prefetch.js +2 -1
  8. package/dist/adapters/alignment/runner.js +1 -1
  9. package/dist/adapters/alignment/worker.js +3 -2
  10. package/dist/adapters/explain.js +10 -12
  11. package/dist/adapters/ffmpeg.js +19 -1
  12. package/dist/adapters/host-cli/index.js +21 -4
  13. package/dist/adapters/host-cli/transport.js +2 -1
  14. package/dist/adapters/http-failure.js +53 -0
  15. package/dist/adapters/image/bytes.js +2 -1
  16. package/dist/adapters/image/codex.js +42 -9
  17. package/dist/adapters/image/fal.js +21 -53
  18. package/dist/adapters/image/google.js +33 -56
  19. package/dist/adapters/image/models.js +2 -2
  20. package/dist/adapters/image/openai.js +22 -48
  21. package/dist/adapters/image/reference.js +36 -0
  22. package/dist/adapters/image/replicate.js +21 -49
  23. package/dist/adapters/image/video.js +2 -1
  24. package/dist/adapters/interest/search-interest.js +113 -0
  25. package/dist/adapters/key-probes.js +54 -3
  26. package/dist/adapters/llm/antigravity-models.js +1 -1
  27. package/dist/adapters/llm/antigravity.js +11 -6
  28. package/dist/adapters/llm/assistant-cli.js +388 -0
  29. package/dist/adapters/llm/child-session.js +83 -0
  30. package/dist/adapters/llm/claude-code-models.js +52 -99
  31. package/dist/adapters/llm/claude-code.js +16 -4
  32. package/dist/adapters/llm/codex-limits.js +27 -68
  33. package/dist/adapters/llm/codex-models.js +1 -1
  34. package/dist/adapters/llm/codex-server-models.js +61 -109
  35. package/dist/adapters/llm/codex.js +12 -1
  36. package/dist/adapters/llm/document-workspace.js +3 -2
  37. package/dist/adapters/llm/image-workspace.js +3 -2
  38. package/dist/adapters/llm/openrouter.js +12 -48
  39. package/dist/adapters/llm/run-cli.js +9 -24
  40. package/dist/adapters/llm/sse-lines.js +5 -1
  41. package/dist/adapters/model-access.js +37 -0
  42. package/dist/adapters/tts/cartesia.js +15 -46
  43. package/dist/adapters/tts/elevenlabs.js +20 -52
  44. package/dist/adapters/tts/gemini.js +21 -41
  45. package/dist/adapters/tts/inworld-async.js +6 -2
  46. package/dist/adapters/tts/inworld.js +6 -2
  47. package/dist/adapters/tts/openai.js +20 -50
  48. package/dist/adapters/tts/system.js +2 -1
  49. package/dist/assets/models-lifecycle.yaml +6 -0
  50. package/dist/assistant-wiring.js +60 -0
  51. package/dist/autopilot-timers.js +59 -0
  52. package/dist/catalog/lifecycle.js +52 -0
  53. package/dist/catalog/propose.js +204 -0
  54. package/dist/catalog/registry.js +23 -14
  55. package/dist/catalog/schema.js +4 -1
  56. package/dist/catalog/store.js +39 -0
  57. package/dist/catalog/validate.js +5 -2
  58. package/dist/edge/autostart/native.js +3 -5
  59. package/dist/edge/autostart/prompt.js +2 -1
  60. package/dist/edge/autostart/service.js +3 -4
  61. package/dist/edge/cli-args.js +3 -4
  62. package/dist/edge/cli.js +2 -1
  63. package/dist/edge/docker-install/apply.js +11 -11
  64. package/dist/edge/docker-install/run.js +2 -1
  65. package/dist/edge/docker-install/state.js +4 -3
  66. package/dist/edge/docker-install/tree.js +2 -4
  67. package/dist/edge/docker-install/volume.js +3 -2
  68. package/dist/edge/docker.js +5 -4
  69. package/dist/edge/events/hub.js +5 -5
  70. package/dist/edge/http/actions.js +11 -32
  71. package/dist/edge/http/actor.js +5 -3
  72. package/dist/edge/http/app.js +86 -11
  73. package/dist/edge/http/assistant.js +129 -0
  74. package/dist/edge/http/audio-preview.js +4 -11
  75. package/dist/edge/http/auditions.js +1 -1
  76. package/dist/edge/http/autostart.js +5 -5
  77. package/dist/edge/http/backups.js +2 -4
  78. package/dist/edge/http/channel-memory.js +9 -22
  79. package/dist/edge/http/channels.js +14 -27
  80. package/dist/edge/http/checkpoints.js +39 -21
  81. package/dist/edge/http/design-templates.js +167 -0
  82. package/dist/edge/http/designs.js +178 -0
  83. package/dist/edge/http/diagnostics.js +5 -1
  84. package/dist/edge/http/document-themes.js +5 -14
  85. package/dist/edge/http/draft-files.js +1 -3
  86. package/dist/edge/http/draft-problem.js +5 -10
  87. package/dist/edge/http/drafts.js +3 -3
  88. package/dist/edge/http/entries.js +6 -9
  89. package/dist/edge/http/extension-auth.js +27 -0
  90. package/dist/edge/http/external-reviews.js +12 -11
  91. package/dist/edge/http/files.js +5 -11
  92. package/dist/edge/http/folder-location.js +1 -4
  93. package/dist/edge/http/fonts.js +0 -7
  94. package/dist/edge/http/home.js +47 -14
  95. package/dist/edge/http/host-cli.js +18 -8
  96. package/dist/edge/http/ids.js +8 -0
  97. package/dist/edge/http/keywords.js +35 -0
  98. package/dist/edge/http/model-checks.js +132 -0
  99. package/dist/edge/http/narration-peaks.js +5 -13
  100. package/dist/edge/http/onboarding.js +30 -26
  101. package/dist/edge/http/open-folder.js +5 -10
  102. package/dist/edge/http/outputs.js +7 -14
  103. package/dist/edge/http/patch-notes.js +6 -12
  104. package/dist/edge/http/planning.js +3 -7
  105. package/dist/edge/http/problem.js +2 -4
  106. package/dist/edge/http/project-create.js +2 -1
  107. package/dist/edge/http/project-templates.js +31 -19
  108. package/dist/edge/http/projects.js +24 -16
  109. package/dist/edge/http/prompts.js +9 -16
  110. package/dist/edge/http/pronunciations.js +1 -2
  111. package/dist/edge/http/providers.js +48 -34
  112. package/dist/edge/http/refusal.js +29 -0
  113. package/dist/edge/http/remote-access.js +40 -0
  114. package/dist/edge/http/remote-guard.js +71 -0
  115. package/dist/edge/http/request-guard.js +91 -0
  116. package/dist/edge/http/reviews.js +12 -25
  117. package/dist/edge/http/revision-files.js +6 -11
  118. package/dist/edge/http/revisions.js +14 -19
  119. package/dist/edge/http/run-cost.js +1 -2
  120. package/dist/edge/http/same-page.js +80 -0
  121. package/dist/edge/http/schedules.js +69 -40
  122. package/dist/edge/http/settings.js +27 -14
  123. package/dist/edge/http/social.js +283 -0
  124. package/dist/edge/http/staging.js +3 -5
  125. package/dist/edge/http/storage-files.js +5 -7
  126. package/dist/edge/http/storage.js +33 -25
  127. package/dist/edge/http/studio.js +94 -69
  128. package/dist/edge/http/style-preview.js +3 -6
  129. package/dist/edge/http/subtitles.js +3 -17
  130. package/dist/edge/http/template-studio.js +436 -0
  131. package/dist/edge/http/trash.js +11 -12
  132. package/dist/edge/http/tutorial.js +1 -2
  133. package/dist/edge/http/tutorials.js +5 -11
  134. package/dist/edge/http/update.js +7 -13
  135. package/dist/edge/http/waveform.js +3 -6
  136. package/dist/edge/http/youtube-edits.js +3 -4
  137. package/dist/edge/mcp/assistant.js +279 -0
  138. package/dist/edge/mcp/attribution.js +8 -2
  139. package/dist/edge/mcp/call-route.js +11 -2
  140. package/dist/edge/mcp/excluded-routes.js +29 -3
  141. package/dist/edge/mcp/http.js +11 -2
  142. package/dist/edge/mcp/instructions.js +6 -4
  143. package/dist/edge/mcp/library-tools.js +9 -5
  144. package/dist/edge/mcp/remake-tools.js +7 -4
  145. package/dist/edge/mcp/route-tool.js +35 -15
  146. package/dist/edge/mcp/server.js +13 -8
  147. package/dist/edge/mcp/tools-channels.js +11 -9
  148. package/dist/edge/mcp/tools-designs.js +165 -0
  149. package/dist/edge/mcp/tools-library.js +19 -7
  150. package/dist/edge/mcp/tools-planning.js +40 -21
  151. package/dist/edge/mcp/tools-projects.js +38 -11
  152. package/dist/edge/mcp/tools-publishing.js +9 -9
  153. package/dist/edge/mcp/tools-runs.js +23 -10
  154. package/dist/edge/mcp/tools-schedules.js +32 -8
  155. package/dist/edge/mcp/tools-setup.js +75 -11
  156. package/dist/edge/mcp/tools-social.js +47 -0
  157. package/dist/edge/mcp/tools-storage.js +11 -3
  158. package/dist/edge/mcp/tools-template-studio.js +166 -0
  159. package/dist/edge/mcp/tools.js +6 -0
  160. package/dist/edge/remote-access/cli.js +71 -0
  161. package/dist/edge/remote-access/model.js +32 -0
  162. package/dist/edge/remote-access/rules.js +207 -0
  163. package/dist/edge/remote-access/service.js +203 -0
  164. package/dist/edge/remote-access/store.js +44 -0
  165. package/dist/edge/remote-access/views.js +43 -0
  166. package/dist/extension/slopify-studio-chrome.zip +0 -0
  167. package/dist/extension/slopify-studio-firefox.zip +0 -0
  168. package/dist/host-cli/install.js +2 -2
  169. package/dist/host-cli/open-folder.js +2 -2
  170. package/dist/host-cli/paths.js +2 -4
  171. package/dist/host-cli/server.js +4 -4
  172. package/dist/host-cli/service.js +3 -3
  173. package/dist/host-cli/status.js +7 -7
  174. package/dist/kernel/db/migrations/0063-model-checks.sql +20 -0
  175. package/dist/kernel/db/migrations/0064-template-studio.sql +46 -0
  176. package/dist/kernel/db/migrations/0065-promise-and-assistant-reviews.sql +58 -0
  177. package/dist/kernel/db/migrations/0066-assistant.sql +44 -0
  178. package/dist/kernel/db/migrations/0067-autopilot.sql +6 -0
  179. package/dist/kernel/db/migrations/0068-topic-scores.sql +17 -0
  180. package/dist/kernel/db/migrations/0070-designs.sql +53 -0
  181. package/dist/kernel/db/migrations/0071-design-usage.sql +33 -0
  182. package/dist/kernel/db/migrations/0072-design-templates.sql +33 -0
  183. package/dist/kernel/db/migrations/0073-schedule-design-kind.sql +5 -0
  184. package/dist/kernel/db/migrations/0074-template-bakeoffs.sql +44 -0
  185. package/dist/kernel/db/migrations/0075-social-posts.sql +25 -0
  186. package/dist/kernel/db/migrations/0076-channel-social.sql +5 -0
  187. package/dist/kernel/db/migrations/0077-post-prompts.sql +17 -0
  188. package/dist/kernel/db/migrations/0078-uploaded-from-links.sql +7 -0
  189. package/dist/kernel/db/tx.js +2 -4
  190. package/dist/kernel/errors.js +27 -1
  191. package/dist/kernel/json-answer.js +15 -0
  192. package/dist/kernel/lock.js +4 -9
  193. package/dist/kernel/paths.js +8 -0
  194. package/dist/kernel/pipeline.js +12 -0
  195. package/dist/kernel/ports/assistant-cli.js +4 -0
  196. package/dist/kernel/ports/host-cli.js +3 -0
  197. package/dist/kernel/ports/interest.js +4 -0
  198. package/dist/kernel/ports/languages.js +0 -3
  199. package/dist/kernel/ports/model.js +2 -0
  200. package/dist/kernel/ports/settings-places.js +31 -0
  201. package/dist/kernel/ports/system-speech.js +31 -3
  202. package/dist/kernel/runner/attempt.js +14 -4
  203. package/dist/kernel/runner/audition.js +3 -1
  204. package/dist/kernel/runner/checkpoint-authority.js +2 -1
  205. package/dist/kernel/runner/index.js +8 -10
  206. package/dist/kernel/runner/model-access.js +40 -0
  207. package/dist/kernel/runner/model-check.js +153 -0
  208. package/dist/kernel/runner/providers.js +18 -12
  209. package/dist/kernel/runner/standalone.js +11 -7
  210. package/dist/main.js +338 -21
  211. package/dist/model-catalog.js +20 -0
  212. package/dist/model-checks.js +166 -0
  213. package/dist/patch-notes/4.0.0.md +51 -0
  214. package/dist/patch-notes/4.1.0.md +42 -0
  215. package/dist/patch-notes/index.json +12 -0
  216. package/dist/slices/admission/heads.js +2 -1
  217. package/dist/slices/admission/listing-facts.js +95 -0
  218. package/dist/slices/admission/repo.js +11 -7
  219. package/dist/slices/admission/rules.js +50 -15
  220. package/dist/slices/admission/schema.js +8 -0
  221. package/dist/slices/admission/stage-writes.js +0 -3
  222. package/dist/slices/admission/start.js +48 -31
  223. package/dist/slices/admission/state-words.js +34 -5
  224. package/dist/slices/admission/substitute.js +7 -4
  225. package/dist/slices/agents/attention-posts.js +31 -0
  226. package/dist/slices/agents/attention-templates.js +33 -0
  227. package/dist/slices/agents/attention.js +10 -1
  228. package/dist/slices/agents/connection.js +2 -6
  229. package/dist/slices/agents/model.js +6 -3
  230. package/dist/slices/agents/repo.js +15 -7
  231. package/dist/slices/article/continuation.js +0 -63
  232. package/dist/slices/article/plain.js +3 -2
  233. package/dist/slices/article/segments.js +2 -62
  234. package/dist/slices/assistant/access.js +1 -0
  235. package/dist/slices/assistant/asks.js +80 -0
  236. package/dist/slices/assistant/cost.js +54 -0
  237. package/dist/slices/assistant/gate.js +222 -0
  238. package/dist/slices/assistant/model.js +86 -0
  239. package/dist/slices/assistant/names.js +23 -0
  240. package/dist/slices/assistant/prompt.js +74 -0
  241. package/dist/slices/assistant/repo.js +74 -0
  242. package/dist/slices/assistant/tokens.js +18 -0
  243. package/dist/slices/assistant/turn.js +344 -0
  244. package/dist/slices/autopilot/buffer.js +76 -0
  245. package/dist/slices/autopilot/model.js +14 -0
  246. package/dist/slices/autopilot/notify.js +27 -0
  247. package/dist/slices/autopilot/repo.js +34 -0
  248. package/dist/slices/autopilot/signals.js +194 -0
  249. package/dist/slices/autopilot/warnings.js +85 -0
  250. package/dist/slices/autopilot/weekly.js +100 -0
  251. package/dist/slices/backups/files.js +2 -6
  252. package/dist/slices/backups/repo.js +3 -15
  253. package/dist/slices/backups/service.js +8 -7
  254. package/dist/slices/channels/cast-images.js +3 -2
  255. package/dist/slices/channels/model.js +0 -1
  256. package/dist/slices/channels/rebrand.js +0 -3
  257. package/dist/slices/channels/repo.js +4 -0
  258. package/dist/slices/channels/schema.js +7 -1
  259. package/dist/slices/channels/service.js +10 -0
  260. package/dist/slices/channels/videos.js +3 -15
  261. package/dist/slices/checkpoints/index.js +0 -1
  262. package/dist/slices/control/providers.js +2 -28
  263. package/dist/slices/designs/artwork.js +66 -0
  264. package/dist/slices/designs/budget.js +23 -0
  265. package/dist/slices/designs/calls.js +124 -0
  266. package/dist/slices/designs/check-context.js +38 -0
  267. package/dist/slices/designs/check-text.js +43 -0
  268. package/dist/slices/designs/fields.js +88 -0
  269. package/dist/slices/designs/frames.js +34 -0
  270. package/dist/slices/designs/gate.js +130 -0
  271. package/dist/slices/designs/layout.js +293 -0
  272. package/dist/slices/designs/model.js +35 -0
  273. package/dist/slices/designs/pdf-job.js +23 -0
  274. package/dist/slices/designs/pdf-pages.js +30 -0
  275. package/dist/slices/designs/pdf-runner.js +68 -0
  276. package/dist/slices/designs/pdf-worker.js +24 -0
  277. package/dist/slices/designs/picture-size.js +79 -0
  278. package/dist/slices/designs/pictures.js +55 -0
  279. package/dist/slices/designs/quote.js +24 -0
  280. package/dist/slices/designs/render-args.js +36 -0
  281. package/dist/slices/designs/render-input.js +59 -0
  282. package/dist/slices/designs/render.js +215 -0
  283. package/dist/slices/designs/repo.js +101 -0
  284. package/dist/slices/designs/requests.js +80 -0
  285. package/dist/slices/designs/runner.js +239 -0
  286. package/dist/slices/designs/sample-repo.js +91 -0
  287. package/dist/slices/designs/sample.js +223 -0
  288. package/dist/slices/designs/schema.js +87 -0
  289. package/dist/slices/designs/sections.js +44 -0
  290. package/dist/slices/designs/service.js +233 -0
  291. package/dist/slices/designs/steps-repo.js +102 -0
  292. package/dist/slices/designs/studio-kind.js +39 -0
  293. package/dist/slices/designs/template-checks.js +44 -0
  294. package/dist/slices/designs/template-make.js +118 -0
  295. package/dist/slices/designs/templates.js +218 -0
  296. package/dist/slices/document/fonts.js +2 -1
  297. package/dist/slices/document/model.js +7 -4
  298. package/dist/slices/document/render.js +2 -1
  299. package/dist/slices/document/theme-schema.js +1 -1
  300. package/dist/slices/document/theme.js +49 -3
  301. package/dist/slices/episodes/summarize.js +6 -2
  302. package/dist/slices/estimate/index.js +72 -28
  303. package/dist/slices/estimate/requests.js +4 -4
  304. package/dist/slices/fixes/rules.js +3 -1
  305. package/dist/slices/fonts/catalog.js +5 -3
  306. package/dist/slices/fonts/coverage.js +2 -1
  307. package/dist/slices/fonts/discovery.js +3 -2
  308. package/dist/slices/fonts/files.js +2 -6
  309. package/dist/slices/fonts/index.js +0 -1
  310. package/dist/slices/fonts/sfnt.js +45 -10
  311. package/dist/slices/images/appearance.js +8 -16
  312. package/dist/slices/images/scenes.js +102 -28
  313. package/dist/slices/keywords/digest.js +85 -0
  314. package/dist/slices/keywords/facts.js +92 -0
  315. package/dist/slices/keywords/fill.js +80 -0
  316. package/dist/slices/keywords/model.js +75 -0
  317. package/dist/slices/keywords/rules.js +187 -0
  318. package/dist/slices/keywords/schema.js +28 -0
  319. package/dist/slices/keywords/specs.js +260 -0
  320. package/dist/slices/keywords/values.js +74 -0
  321. package/dist/slices/library/lint.js +29 -12
  322. package/dist/slices/library/model.js +2 -0
  323. package/dist/slices/library/photorealistic.js +4 -12
  324. package/dist/slices/library/rename.js +9 -7
  325. package/dist/slices/library/save.js +14 -10
  326. package/dist/slices/library/slots.js +37 -11
  327. package/dist/slices/library/transfer.js +5 -4
  328. package/dist/slices/library/used-by.js +25 -10
  329. package/dist/slices/loudness/loudnorm.js +53 -12
  330. package/dist/slices/loudness/model.js +3 -2
  331. package/dist/slices/loudness/sentence-level.js +2 -1
  332. package/dist/slices/model-checks/cost.js +61 -0
  333. package/dist/slices/model-checks/listing.js +124 -0
  334. package/dist/slices/model-checks/model.js +20 -0
  335. package/dist/slices/model-checks/record.js +32 -0
  336. package/dist/slices/model-checks/repo.js +120 -0
  337. package/dist/slices/model-checks/rules.js +125 -0
  338. package/dist/slices/model-checks/run.js +143 -0
  339. package/dist/slices/model-checks/service.js +198 -0
  340. package/dist/slices/model-upkeep/model.js +1 -1
  341. package/dist/slices/model-upkeep/switch.js +72 -27
  342. package/dist/slices/model-upkeep/usage.js +39 -13
  343. package/dist/slices/narration/breaths.js +3 -1
  344. package/dist/slices/narration/clean-pieces.js +3 -1
  345. package/dist/slices/narration/concat.js +2 -1
  346. package/dist/slices/narration/pauses-model.js +3 -2
  347. package/dist/slices/narration/peaks.js +3 -4
  348. package/dist/slices/narration/pronunciation-chunks.js +4 -3
  349. package/dist/slices/narration/pronunciation.js +6 -4
  350. package/dist/slices/narration/steering.js +4 -3
  351. package/dist/slices/notifications/notifier.js +4 -3
  352. package/dist/slices/notifications/rules.js +5 -2
  353. package/dist/slices/notifications/settings.js +4 -12
  354. package/dist/slices/notifications/subject.js +4 -3
  355. package/dist/slices/onboarding/copy.js +21 -3
  356. package/dist/slices/onboarding/first-run.js +16 -40
  357. package/dist/slices/onboarding/full-video.js +3 -2
  358. package/dist/slices/onboarding/install.js +6 -4
  359. package/dist/slices/onboarding/model.js +26 -37
  360. package/dist/slices/onboarding/packs.js +22 -22
  361. package/dist/slices/onboarding/preferred-model.js +14 -0
  362. package/dist/slices/onboarding/quick-short.js +105 -65
  363. package/dist/slices/onboarding/sample.js +4 -2
  364. package/dist/slices/onboarding/state.js +9 -11
  365. package/dist/slices/onboarding/template.js +8 -6
  366. package/dist/slices/outputs/add.js +3 -2
  367. package/dist/slices/outputs/service.js +2 -2
  368. package/dist/slices/patch-notes/library.js +4 -3
  369. package/dist/slices/play-drafts/convert.js +4 -0
  370. package/dist/slices/play-drafts/readiness.js +3 -3
  371. package/dist/slices/play-drafts/review-inputs.js +9 -3
  372. package/dist/slices/play-drafts/review.js +5 -4
  373. package/dist/slices/play-drafts/schema.js +4 -0
  374. package/dist/slices/play-drafts/service.js +17 -7
  375. package/dist/slices/project-templates/from-project.js +9 -2
  376. package/dist/slices/project-templates/next-chapter.js +2 -1
  377. package/dist/slices/project-templates/provider-defaults.js +22 -0
  378. package/dist/slices/project-templates/repo.js +6 -1
  379. package/dist/slices/project-templates/setup-sections.js +26 -0
  380. package/dist/slices/project-templates/setup.js +14 -1
  381. package/dist/slices/project-templates/stand-ins.js +22 -0
  382. package/dist/slices/rebuild/activity.js +2 -0
  383. package/dist/slices/rebuild/admission-repo.js +3 -2
  384. package/dist/slices/rebuild/narration-history.js +4 -5
  385. package/dist/slices/rebuild/narration-retry.js +2 -1
  386. package/dist/slices/rebuild/preview-details.js +32 -14
  387. package/dist/slices/rebuild/preview-plan.js +3 -8
  388. package/dist/slices/rebuild/recipe-build.js +8 -3
  389. package/dist/slices/rebuild/recipe-describe.js +3 -3
  390. package/dist/slices/rebuild/recipe-digest.js +58 -0
  391. package/dist/slices/rebuild/recipe-input-schema.js +10 -0
  392. package/dist/slices/rebuild/recipe-keywords.js +71 -0
  393. package/dist/slices/rebuild/recipe-legacy.js +5 -1
  394. package/dist/slices/rebuild/recipe-model.js +4 -0
  395. package/dist/slices/rebuild/recipe-narration-text.js +2 -1
  396. package/dist/slices/rebuild/recipe-preparation.js +4 -4
  397. package/dist/slices/rebuild/recipe-reference.js +8 -4
  398. package/dist/slices/rebuild/recipe-reviews.js +27 -7
  399. package/dist/slices/rebuild/recipe-save.js +8 -5
  400. package/dist/slices/rebuild/recipe-scenes.js +40 -11
  401. package/dist/slices/rebuild/recipe-shorts.js +8 -8
  402. package/dist/slices/rebuild/recipe-social.js +46 -0
  403. package/dist/slices/rebuild/recipe-text.js +64 -20
  404. package/dist/slices/rebuild/recipe-validation.js +102 -31
  405. package/dist/slices/rebuild/recipe-visual.js +55 -16
  406. package/dist/slices/rebuild/recipe-voices.js +3 -2
  407. package/dist/slices/rebuild/recipe-work.js +19 -7
  408. package/dist/slices/rebuild/recipe-youtube.js +4 -3
  409. package/dist/slices/rebuild/recovery-selection.js +3 -1
  410. package/dist/slices/rebuild/recovery.js +9 -8
  411. package/dist/slices/rebuild/remake-outdated.js +5 -5
  412. package/dist/slices/rebuild/repo.js +4 -3
  413. package/dist/slices/rebuild/review-promise.js +25 -0
  414. package/dist/slices/rebuild/review-redo.js +3 -2
  415. package/dist/slices/rebuild/runtime-admission.js +4 -3
  416. package/dist/slices/rebuild/runtime-agent-review.js +11 -5
  417. package/dist/slices/rebuild/runtime-animate.js +10 -9
  418. package/dist/slices/rebuild/runtime-article.js +7 -5
  419. package/dist/slices/rebuild/runtime-checkpoints.js +5 -4
  420. package/dist/slices/rebuild/runtime-document.js +11 -13
  421. package/dist/slices/rebuild/runtime-export-bed.js +8 -12
  422. package/dist/slices/rebuild/runtime-export-edit.js +8 -16
  423. package/dist/slices/rebuild/runtime-export-inputs.js +12 -11
  424. package/dist/slices/rebuild/runtime-export-short.js +9 -5
  425. package/dist/slices/rebuild/runtime-export.js +38 -15
  426. package/dist/slices/rebuild/runtime-figure-card.js +2 -1
  427. package/dist/slices/rebuild/runtime-image.js +8 -12
  428. package/dist/slices/rebuild/runtime-instructions.js +2 -0
  429. package/dist/slices/rebuild/runtime-keywords.js +59 -0
  430. package/dist/slices/rebuild/runtime-lines.js +2 -11
  431. package/dist/slices/rebuild/runtime-local.js +17 -22
  432. package/dist/slices/rebuild/runtime-narration-publication.js +2 -5
  433. package/dist/slices/rebuild/runtime-narration-reuse.js +9 -13
  434. package/dist/slices/rebuild/runtime-narration-text.js +13 -17
  435. package/dist/slices/rebuild/runtime-pictures.js +2 -4
  436. package/dist/slices/rebuild/runtime-piece-label.js +10 -6
  437. package/dist/slices/rebuild/runtime-plan.js +31 -5
  438. package/dist/slices/rebuild/runtime-provider.js +67 -25
  439. package/dist/slices/rebuild/runtime-publication.js +10 -9
  440. package/dist/slices/rebuild/runtime-review.js +39 -33
  441. package/dist/slices/rebuild/runtime-run.js +4 -1
  442. package/dist/slices/rebuild/runtime-shorts.js +31 -50
  443. package/dist/slices/rebuild/runtime-store.js +20 -10
  444. package/dist/slices/rebuild/runtime-subtitles.js +28 -25
  445. package/dist/slices/rebuild/runtime-voices.js +10 -16
  446. package/dist/slices/rebuild/runtime-youtube.js +14 -31
  447. package/dist/slices/rebuild/service-readiness.js +11 -3
  448. package/dist/slices/rebuild/transition-repo.js +6 -5
  449. package/dist/slices/rebuild/work-records.js +6 -0
  450. package/dist/slices/reruns/cascade.js +1 -2
  451. package/dist/slices/reruns/index.js +6 -9
  452. package/dist/slices/research/synthesis.js +3 -2
  453. package/dist/slices/reviews/external-model.js +4 -1
  454. package/dist/slices/reviews/external-repo.js +2 -1
  455. package/dist/slices/reviews/external-rows.js +2 -3
  456. package/dist/slices/reviews/holds.js +5 -4
  457. package/dist/slices/reviews/model.js +32 -11
  458. package/dist/slices/reviews/repo.js +2 -1
  459. package/dist/slices/reviews/rules.js +4 -3
  460. package/dist/slices/reviews/verdict.js +2 -1
  461. package/dist/slices/reviews/view.js +5 -2
  462. package/dist/slices/revisions/downloads.js +4 -5
  463. package/dist/slices/revisions/image-plan.js +1 -1
  464. package/dist/slices/revisions/mutation-assets.js +9 -5
  465. package/dist/slices/revisions/mutation-cues.js +2 -5
  466. package/dist/slices/revisions/mutations.js +3 -3
  467. package/dist/slices/revisions/repo.js +3 -1
  468. package/dist/slices/revisions/schema.js +1 -0
  469. package/dist/slices/revisions/view.js +20 -0
  470. package/dist/slices/run-cost/limits.js +5 -2
  471. package/dist/slices/run-cost/meter.js +12 -5
  472. package/dist/slices/run-cost/panel.js +8 -74
  473. package/dist/slices/run-cost/plan-readings.js +127 -0
  474. package/dist/slices/run-cost/plan-use.js +169 -0
  475. package/dist/slices/run-cost/pricing.js +3 -3
  476. package/dist/slices/run-cost/week.js +32 -4
  477. package/dist/slices/schedules/agenda.js +18 -7
  478. package/dist/slices/schedules/by-date.js +42 -0
  479. package/dist/slices/schedules/calendar.js +5 -0
  480. package/dist/slices/schedules/design-agenda.js +36 -0
  481. package/dist/slices/schedules/design-check.js +41 -0
  482. package/dist/slices/schedules/design-run.js +126 -0
  483. package/dist/slices/schedules/next-run.js +62 -0
  484. package/dist/slices/schedules/prepare.js +18 -2
  485. package/dist/slices/schedules/repo.js +30 -21
  486. package/dist/slices/schedules/rescore.js +35 -0
  487. package/dist/slices/schedules/scheduler.js +13 -3
  488. package/dist/slices/schedules/schema.js +57 -3
  489. package/dist/slices/schedules/score.js +107 -0
  490. package/dist/slices/schedules/service.js +57 -24
  491. package/dist/slices/schedules/topic-list.js +15 -0
  492. package/dist/slices/schedules/topic-scores.js +104 -0
  493. package/dist/slices/schedules/topics.js +53 -41
  494. package/dist/slices/settings/cli-paths.js +4 -6
  495. package/dist/slices/settings/first-run.js +8 -43
  496. package/dist/slices/settings/health.js +30 -15
  497. package/dist/slices/settings/key-test.js +1 -1
  498. package/dist/slices/settings/model-reach.js +3 -17
  499. package/dist/slices/settings/model.js +14 -2
  500. package/dist/slices/settings/playback.js +34 -23
  501. package/dist/slices/settings/probe-read.js +16 -0
  502. package/dist/slices/settings/repo.js +33 -0
  503. package/dist/slices/settings/tutorial.js +2 -2
  504. package/dist/slices/settings/voices.js +4 -12
  505. package/dist/slices/shorts/keep.js +2 -1
  506. package/dist/slices/shorts/pick.js +6 -18
  507. package/dist/slices/shorts/prompts.js +4 -14
  508. package/dist/slices/shorts/render.js +6 -2
  509. package/dist/slices/social/channel.js +44 -0
  510. package/dist/slices/social/defaults.js +17 -0
  511. package/dist/slices/social/due.js +36 -0
  512. package/dist/slices/social/extension.js +18 -0
  513. package/dist/slices/social/fit.js +115 -0
  514. package/dist/slices/social/limits.js +186 -0
  515. package/dist/slices/social/links.js +21 -0
  516. package/dist/slices/social/media.js +132 -0
  517. package/dist/slices/social/model.js +75 -0
  518. package/dist/slices/social/notice.js +19 -0
  519. package/dist/slices/social/pack.js +176 -0
  520. package/dist/slices/social/post-prompt.js +56 -0
  521. package/dist/slices/social/queue.js +31 -0
  522. package/dist/slices/social/repo.js +133 -0
  523. package/dist/slices/social/reshape.js +97 -0
  524. package/dist/slices/social/rules.js +5 -0
  525. package/dist/slices/social/schema.js +70 -0
  526. package/dist/slices/social/serve.js +51 -0
  527. package/dist/slices/social/service.js +129 -0
  528. package/dist/slices/social/settings.js +21 -0
  529. package/dist/slices/social/sources.js +114 -0
  530. package/dist/slices/social/state.js +110 -0
  531. package/dist/slices/social/text.js +149 -0
  532. package/dist/slices/storage/arrange.js +3 -1
  533. package/dist/slices/storage/assets.js +8 -10
  534. package/dist/slices/storage/backup-designs.js +74 -0
  535. package/dist/slices/storage/backup-export.js +8 -3
  536. package/dist/slices/storage/backup-format.js +10 -0
  537. package/dist/slices/storage/backup-import.js +22 -4
  538. package/dist/slices/storage/delete-project.js +6 -5
  539. package/dist/slices/storage/files-location.js +6 -8
  540. package/dist/slices/storage/layout.js +13 -2
  541. package/dist/slices/storage/model.js +3 -0
  542. package/dist/slices/storage/portable.js +17 -12
  543. package/dist/slices/storage/schema.js +2 -0
  544. package/dist/slices/storage/staging-refs.js +2 -1
  545. package/dist/slices/storage/staging.js +6 -9
  546. package/dist/slices/storage/trim.js +26 -9
  547. package/dist/slices/studio/calendar.js +7 -2
  548. package/dist/slices/studio/disclosure.js +4 -4
  549. package/dist/slices/studio/model.js +1 -1
  550. package/dist/slices/studio/pack.js +9 -8
  551. package/dist/slices/studio/pick.js +2 -11
  552. package/dist/slices/studio/plan.js +7 -16
  553. package/dist/slices/studio/queue.js +3 -14
  554. package/dist/slices/studio/releases.js +26 -0
  555. package/dist/slices/studio/settings.js +20 -57
  556. package/dist/slices/studio/videos.js +35 -4
  557. package/dist/slices/style-preview/render.js +37 -16
  558. package/dist/slices/style-preview/schema.js +14 -1
  559. package/dist/slices/style-preview/service.js +2 -1
  560. package/dist/slices/style-preview/settings.js +33 -5
  561. package/dist/slices/subtitles/captions.js +1 -1
  562. package/dist/slices/telemetry/collector-client.js +2 -4
  563. package/dist/slices/telemetry/flush.js +2 -4
  564. package/dist/slices/telemetry/model.js +0 -7
  565. package/dist/slices/telemetry/record.js +10 -3
  566. package/dist/slices/template-studio/anatomy-view.js +161 -0
  567. package/dist/slices/template-studio/anatomy.js +73 -0
  568. package/dist/slices/template-studio/bakeoff-make.js +225 -0
  569. package/dist/slices/template-studio/bakeoff-repo.js +83 -0
  570. package/dist/slices/template-studio/bakeoff.js +333 -0
  571. package/dist/slices/template-studio/budget.js +106 -0
  572. package/dist/slices/template-studio/cards.js +78 -0
  573. package/dist/slices/template-studio/checks.js +322 -0
  574. package/dist/slices/template-studio/document-sample.js +56 -0
  575. package/dist/slices/template-studio/edit.js +15 -0
  576. package/dist/slices/template-studio/for-all.js +161 -0
  577. package/dist/slices/template-studio/free-video.js +31 -0
  578. package/dist/slices/template-studio/free.js +176 -0
  579. package/dist/slices/template-studio/gate.js +40 -0
  580. package/dist/slices/template-studio/hidden.js +20 -0
  581. package/dist/slices/template-studio/jobs.js +79 -0
  582. package/dist/slices/template-studio/loudness.js +111 -0
  583. package/dist/slices/template-studio/make.js +40 -0
  584. package/dist/slices/template-studio/model.js +58 -0
  585. package/dist/slices/template-studio/pack-import.js +253 -0
  586. package/dist/slices/template-studio/pack-samples.js +60 -0
  587. package/dist/slices/template-studio/pack.js +207 -0
  588. package/dist/slices/template-studio/paid-view.js +66 -0
  589. package/dist/slices/template-studio/paid.js +355 -0
  590. package/dist/slices/template-studio/quote.js +155 -0
  591. package/dist/slices/template-studio/ready.js +104 -0
  592. package/dist/slices/template-studio/repo.js +134 -0
  593. package/dist/slices/template-studio/reviews-path.js +58 -0
  594. package/dist/slices/template-studio/runner.js +84 -0
  595. package/dist/slices/template-studio/save.js +236 -0
  596. package/dist/slices/template-studio/say-it-as.js +248 -0
  597. package/dist/slices/template-studio/schedule-gate.js +34 -0
  598. package/dist/slices/template-studio/schedule-price.js +32 -0
  599. package/dist/slices/template-studio/schedule-sample.js +91 -0
  600. package/dist/slices/template-studio/schema.js +243 -0
  601. package/dist/slices/template-studio/scope.js +124 -0
  602. package/dist/slices/template-studio/sections.js +176 -0
  603. package/dist/slices/template-studio/service.js +175 -0
  604. package/dist/slices/template-studio/status.js +191 -0
  605. package/dist/slices/template-studio/topic.js +223 -0
  606. package/dist/slices/thumbnail/by-llm.js +1 -1
  607. package/dist/slices/trash/designs.js +76 -0
  608. package/dist/slices/trash/model.js +13 -4
  609. package/dist/slices/trash/service.js +52 -2
  610. package/dist/slices/tutorials/library.js +3 -2
  611. package/dist/slices/uploads/repo.js +37 -1
  612. package/dist/slices/uploads/rules.js +3 -2
  613. package/dist/slices/video/ass-text.js +17 -0
  614. package/dist/slices/video/audio-export.js +2 -64
  615. package/dist/slices/video/edit-list.js +2 -2
  616. package/dist/slices/video/edit-settings.js +1 -1
  617. package/dist/slices/video/encoding.js +169 -0
  618. package/dist/slices/video/ffmpeg.js +23 -21
  619. package/dist/slices/video/figure-card.js +1 -15
  620. package/dist/slices/video/plan.js +2 -1
  621. package/dist/slices/video/slideshow.js +43 -18
  622. package/dist/slices/voices/attribution.js +3 -2
  623. package/dist/slices/voices/model.js +9 -8
  624. package/dist/slices/voices/palette.js +0 -3
  625. package/dist/slices/voices/script.js +2 -1
  626. package/dist/slices/youtube/answer.js +4 -16
  627. package/dist/slices/youtube/edits-repo.js +3 -12
  628. package/dist/tutorials/AI-CLIs.md +25 -25
  629. package/dist/tutorials/Agents-MCP.md +34 -33
  630. package/dist/tutorials/Assistant.md +42 -0
  631. package/dist/tutorials/Backups.md +7 -7
  632. package/dist/tutorials/Calendar.md +17 -25
  633. package/dist/tutorials/Cast-Library.md +19 -19
  634. package/dist/tutorials/Channels.md +42 -35
  635. package/dist/tutorials/Costs-and-Run-Cost.md +38 -40
  636. package/dist/tutorials/Create-Article.md +114 -0
  637. package/dist/tutorials/Create-Extras.md +112 -0
  638. package/dist/tutorials/Create-Images.md +165 -0
  639. package/dist/tutorials/Create-Narration.md +178 -0
  640. package/dist/tutorials/Create-Overview.md +244 -0
  641. package/dist/tutorials/Create-Video-and-Style.md +144 -0
  642. package/dist/tutorials/Docker.md +3 -3
  643. package/dist/tutorials/Document-Themes.md +32 -32
  644. package/dist/tutorials/Editing-a-Project.md +94 -94
  645. package/dist/tutorials/FAQ.md +18 -18
  646. package/dist/tutorials/First-Launch-and-Welcome.md +50 -64
  647. package/dist/tutorials/Home-and-Projects.md +115 -71
  648. package/dist/tutorials/Home.md +33 -33
  649. package/dist/tutorials/Install.md +14 -13
  650. package/dist/tutorials/Intros-and-Outros.md +33 -34
  651. package/dist/tutorials/Keyboard-Shortcuts-and-Command-Palette.md +62 -58
  652. package/dist/tutorials/Keywords-Slopify-Fills.md +87 -0
  653. package/dist/tutorials/Models.md +53 -21
  654. package/dist/tutorials/Multiple-Voices.md +41 -40
  655. package/dist/tutorials/Narration-Aliases-and-Glossary.md +14 -14
  656. package/dist/tutorials/Notifications.md +27 -17
  657. package/dist/tutorials/Other-Languages.md +15 -15
  658. package/dist/tutorials/PDF-Documents.md +38 -29
  659. package/dist/tutorials/Posting-to-Social-Sites.md +121 -0
  660. package/dist/tutorials/Project-Page.md +118 -120
  661. package/dist/tutorials/Prompts-and-Templates.md +84 -0
  662. package/dist/tutorials/Prompts.md +48 -44
  663. package/dist/tutorials/Providers-and-Keys.md +105 -98
  664. package/dist/tutorials/Publishing-to-YouTube.md +31 -31
  665. package/dist/tutorials/Recovery-and-Retries.md +31 -31
  666. package/dist/tutorials/Remote-Access.md +63 -0
  667. package/dist/tutorials/Reviews-and-Checkpoints.md +98 -90
  668. package/dist/tutorials/Schedules.md +46 -27
  669. package/dist/tutorials/Settings-Reference.md +194 -174
  670. package/dist/tutorials/Shorts.md +34 -33
  671. package/dist/tutorials/Start-at-Login.md +3 -3
  672. package/dist/tutorials/Studio-Extension.md +22 -24
  673. package/dist/tutorials/Template-Studio.md +128 -0
  674. package/dist/tutorials/Templates.md +51 -77
  675. package/dist/tutorials/Trash.md +4 -4
  676. package/dist/tutorials/Troubleshooting.md +44 -44
  677. package/dist/tutorials/Uninstalling-and-Moving.md +5 -5
  678. package/dist/tutorials/Updating-and-Patch-Notes.md +18 -18
  679. package/dist/tutorials/Video-Editing.md +34 -34
  680. package/dist/tutorials/Where-Your-Files-Live.md +10 -10
  681. package/dist/tutorials/YouTube-Description.md +26 -26
  682. package/dist/tutorials/Your-First-Short.md +35 -35
  683. package/dist/tutorials/_Sidebar.md +19 -14
  684. package/dist/updater/work-in-progress.js +42 -12
  685. package/dist/web/assets/index-BNftKQrA.css +1 -0
  686. package/dist/web/assets/index-Cpa58woP.js +459 -0
  687. package/dist/web/assets/pdf-BKh8f7R6.js +56 -0
  688. package/dist/web/assets/pdf.worker.min-BmVo14Nb.mjs +28 -0
  689. package/dist/web/assets/pdf.worker.min-D7i8o3wR.js +1 -0
  690. package/dist/web/index.html +2 -2
  691. package/package.json +6 -2
  692. package/dist/slices/article/provided-entries.js +0 -66
  693. package/dist/slices/article/run.js +0 -145
  694. package/dist/slices/images/run.js +0 -234
  695. package/dist/slices/narration/legacy-plan.js +0 -58
  696. package/dist/slices/narration/run.js +0 -270
  697. package/dist/slices/rebuild/runtime-actions.js +0 -90
  698. package/dist/slices/research/run.js +0 -224
  699. package/dist/slices/subtitles/prepare.js +0 -194
  700. package/dist/slices/subtitles/transcript.js +0 -34
  701. package/dist/slices/thumbnail/run.js +0 -219
  702. package/dist/slices/video/audio-inputs.js +0 -33
  703. package/dist/slices/video/reuse-audio.js +0 -62
  704. package/dist/slices/video/run.js +0 -78
  705. package/dist/slices/video/write-export.js +0 -158
  706. package/dist/slices/video/write-subtitles.js +0 -59
  707. package/dist/tutorials/Library-Overview.md +0 -76
  708. package/dist/tutorials/Play-Images.md +0 -161
  709. package/dist/tutorials/Play-Narration.md +0 -167
  710. package/dist/tutorials/Play-Outputs.md +0 -109
  711. package/dist/tutorials/Play-Overview.md +0 -227
  712. package/dist/tutorials/Play-Title-and-Article.md +0 -136
  713. package/dist/tutorials/Play-Video-and-Style.md +0 -143
  714. package/dist/web/assets/index-BadsoORk.css +0 -1
  715. package/dist/web/assets/index-D8LqNsCl.js +0 -326
  716. package/dist/web/assets/pdf-T7MCwjIv.js +0 -54
  717. package/dist/web/assets/pdf.worker.min-Dswkl-cV.mjs +0 -29
  718. package/dist/web/assets/pdf.worker.min-zC6nfbxG.js +0 -1
@@ -6,27 +6,31 @@ import { createTemplateFromProject, templateFromProjectSchema, } from "../../sli
6
6
  import { makeNextChapter, nextChapterSchema } from "../../slices/project-templates/next-chapter.js";
7
7
  import { templateCreateSchema, templateDeleteSchema, templateInstantiateSchema, templateUpdateSchema, } from "../../slices/project-templates/schema.js";
8
8
  import { createTemplate, deleteTemplate, instantiateTemplate, listTemplates, readTemplate, updateTemplate, } from "../../slices/project-templates/service.js";
9
- import { onInvalid, problem, titleOf } from "./problem.js";
9
+ import { templateGate } from "../../slices/template-studio/schedule-gate.js";
10
+ import { onInvalid, problem } from "./problem.js";
11
+ import { refusal } from "./refusal.js";
10
12
  const param = z.object({ id: z.uuid() });
13
+ // The JSON bodies; the id or project the path names is not part of them.
14
+ export const templateFromProjectBody = templateFromProjectSchema.unwrap().omit({ projectId: true });
15
+ export const nextChapterBody = nextChapterSchema.unwrap().omit({ projectId: true });
16
+ export const templateUpdateBody = templateUpdateSchema.unwrap().omit({ id: true });
17
+ export const templateDeleteBody = templateDeleteSchema.unwrap().omit({ id: true });
18
+ export const templateInstantiateBody = templateInstantiateSchema
19
+ .unwrap()
20
+ .omit({ templateId: true });
11
21
  function refused(c, result) {
12
- const status = result.reason === "not-found"
13
- ? 404
14
- : ["conflict", "referenced-by-schedule"].includes(result.reason)
15
- ? 409
16
- : 400;
17
- return problem(c, {
18
- status,
19
- title: titleOf(status),
22
+ return refusal(c, result.reason, {
23
+ statuses: { "not-found": 404, conflict: 409, "referenced-by-schedule": 409 },
24
+ otherwise: 400,
20
25
  detail: result.reason === "referenced-by-schedule"
21
- ? "Some schedules still use this template. In Library > Schedules, cancel and delete them first; their run history is kept."
26
+ ? "Some schedules still use this template. In Calendar → Schedules, cancel and delete them first; their run history is kept."
22
27
  : result.reason === "missing-prompt"
23
28
  ? "A prompt or intro/outro entry this template uses was deleted. Choose another in the template, then save again."
24
29
  : result.reason === "not-found"
25
- ? "This template no longer exists; it may have been deleted. Go to Library > Templates to see your templates."
30
+ ? "This template no longer exists; it may have been deleted. Go to Templates to see your templates."
26
31
  : result.reason === "conflict"
27
32
  ? "This template or its project changed while you were editing. Reload the page to see the latest, then make your change again."
28
33
  : "Some template settings are not valid, for example an image you supplied yourself. Check the template's settings and try again.",
29
- extensions: { reason: result.reason },
30
34
  });
31
35
  }
32
36
  export function projectTemplateRoutes(deps) {
@@ -39,8 +43,17 @@ function routes(deps) {
39
43
  return deps;
40
44
  };
41
45
  return (new Hono()
42
- .get("/", (c) => c.json({ templates: listTemplates(service()) }))
43
- .post("/from-project/:projectId", zValidator("param", templateFromProjectSchema.unwrap().pick({ projectId: true }), onInvalid), zValidator("json", templateFromProjectSchema.unwrap().omit({ projectId: true }), onInvalid), (c) => {
46
+ // Each with the version its schedules run (`template-studio/schedule-gate.ts`).
47
+ .get("/", (c) => {
48
+ const db = service().db;
49
+ return c.json({
50
+ templates: listTemplates(service()).map((template) => {
51
+ const gate = templateGate(db, template.id);
52
+ return gate === undefined ? template : { ...template, gate };
53
+ }),
54
+ });
55
+ })
56
+ .post("/from-project/:projectId", zValidator("param", templateFromProjectSchema.unwrap().pick({ projectId: true }), onInvalid), zValidator("json", templateFromProjectBody, onInvalid), (c) => {
44
57
  const result = createTemplateFromProject(service(), {
45
58
  ...c.req.valid("json"),
46
59
  projectId: c.req.valid("param").projectId,
@@ -48,7 +61,7 @@ function routes(deps) {
48
61
  return result.ok ? c.json(result.value, 201) : refused(c, result);
49
62
  })
50
63
  // A finished audiobook chapter's "Make the next chapter": a new Play draft for the book.
51
- .post("/next-chapter/:projectId", zValidator("param", nextChapterSchema.unwrap().pick({ projectId: true }), onInvalid), zValidator("json", nextChapterSchema.unwrap().omit({ projectId: true }), onInvalid), (c) => {
64
+ .post("/next-chapter/:projectId", zValidator("param", nextChapterSchema.unwrap().pick({ projectId: true }), onInvalid), zValidator("json", nextChapterBody, onInvalid), (c) => {
52
65
  const result = makeNextChapter(service(), {
53
66
  ...c.req.valid("json"),
54
67
  projectId: c.req.valid("param").projectId,
@@ -58,7 +71,6 @@ function routes(deps) {
58
71
  const status = result.reason === "not-found" ? 404 : result.reason === "conflict" ? 409 : 400;
59
72
  return problem(c, {
60
73
  status,
61
- title: titleOf(status),
62
74
  detail: result.message,
63
75
  extensions: { reason: result.reason },
64
76
  });
@@ -71,21 +83,21 @@ function routes(deps) {
71
83
  const result = readTemplate(service(), c.req.valid("param").id, c.req.valid("query").version);
72
84
  return result.ok ? c.json(result.value) : refused(c, result);
73
85
  })
74
- .put("/:id", zValidator("param", param, onInvalid), zValidator("json", templateUpdateSchema.unwrap().omit({ id: true }), onInvalid), (c) => {
86
+ .put("/:id", zValidator("param", param, onInvalid), zValidator("json", templateUpdateBody, onInvalid), (c) => {
75
87
  const result = updateTemplate(service(), {
76
88
  ...c.req.valid("json"),
77
89
  id: c.req.valid("param").id,
78
90
  });
79
91
  return result.ok ? c.json(result.value) : refused(c, result);
80
92
  })
81
- .delete("/:id", zValidator("param", param, onInvalid), zValidator("json", templateDeleteSchema.unwrap().omit({ id: true }), onInvalid), (c) => {
93
+ .delete("/:id", zValidator("param", param, onInvalid), zValidator("json", templateDeleteBody, onInvalid), (c) => {
82
94
  const result = deleteTemplate(service(), {
83
95
  ...c.req.valid("json"),
84
96
  id: c.req.valid("param").id,
85
97
  });
86
98
  return result.ok ? c.json(result.value) : refused(c, result);
87
99
  })
88
- .post("/:id/instantiate", zValidator("param", param, onInvalid), zValidator("json", templateInstantiateSchema.unwrap().omit({ templateId: true }), onInvalid), (c) => {
100
+ .post("/:id/instantiate", zValidator("param", param, onInvalid), zValidator("json", templateInstantiateBody, onInvalid), (c) => {
89
101
  const result = instantiateTemplate(service(), {
90
102
  ...c.req.valid("json"),
91
103
  templateId: c.req.valid("param").id,
@@ -4,13 +4,15 @@ import { z } from "zod";
4
4
  import { transact } from "../../kernel/db/tx.js";
5
5
  import { derive, progressOf } from "../../kernel/runner/graph.js";
6
6
  import { listProjectHeads } from "../../slices/admission/heads.js";
7
+ import { listingFacts } from "../../slices/admission/listing-facts.js";
7
8
  import { projectById, projectTrashed, runDraftSchema, stageStandingsByProject, stagesOf, stepFailuresByProject, } from "../../slices/admission/repo.js";
8
- import { readsByWaitBesideFailure, stepFailedWhileWaiting, } from "../../slices/admission/state-words.js";
9
+ import { readsByWaitBesideFailure, retryingAt, stepFailedWhileWaiting, } from "../../slices/admission/state-words.js";
9
10
  import { lastStarter } from "../../slices/agents/repo.js";
10
11
  import { defaultChannelId } from "../../slices/channels/model.js";
11
12
  import { channelById, projectChannels, setProjectChannel } from "../../slices/channels/repo.js";
12
13
  import { withProjectControl } from "../../slices/control/lock.js";
13
14
  import { stagesWithEta } from "../../slices/eta/view.js";
15
+ import { sampleProjectIds } from "../../slices/onboarding/sample.js";
14
16
  import { videoActivity } from "../../slices/rebuild/activity.js";
15
17
  import { projectHolds } from "../../slices/reviews/holds.js";
16
18
  import { adoptBaseline } from "../../slices/revisions/adopt.js";
@@ -18,17 +20,11 @@ import { currentRevisionId } from "../../slices/revisions/repo.js";
18
20
  import { limitWaitsByProject, listingWait } from "../../slices/run-cost/panel.js";
19
21
  import { outputsOf } from "../../slices/storage/repo.js";
20
22
  import { trashProject } from "../../slices/trash/service.js";
21
- import { setAsideProjects, uploadedAndSettled, uploadedProjects, } from "../../slices/uploads/repo.js";
23
+ import { setAsideProjects, settledProjects, uploadedProjects } from "../../slices/uploads/repo.js";
22
24
  import { settledStatus } from "../../slices/uploads/rules.js";
23
- import { onInvalid, problem, titleOf } from "./problem.js";
25
+ import { idParam } from "./ids.js";
26
+ import { onInvalid, problem } from "./problem.js";
24
27
  import { createProject } from "./project-create.js";
25
- const idParam = z.object({
26
- id: z
27
- .string()
28
- .min(1)
29
- .max(64)
30
- .regex(/^[0-9A-Za-z_-]+$/),
31
- });
32
28
  // The return type is inferred so Hono keeps the route types the SPA's client is
33
29
  // generated from; see stagingRoutes.
34
30
  export function projectRoutes(deps) {
@@ -41,7 +37,7 @@ export function projectRoutes(deps) {
41
37
  };
42
38
  const summarise = (project) => {
43
39
  const stages = stagesOf(deps.db, project.id);
44
- const status = settledStatus(derive(stages, project.paused), uploadedAndSettled(deps.db, project.id).has(project.id));
40
+ const status = settledStatus(derive(stages, project.paused), settledProjects(deps.db, project.id).has(project.id));
45
41
  return {
46
42
  ...project,
47
43
  status,
@@ -54,7 +50,6 @@ export function projectRoutes(deps) {
54
50
  if (!created.ok)
55
51
  return problem(c, {
56
52
  status: created.status,
57
- title: titleOf(created.status),
58
53
  detail: created.detail,
59
54
  ...(created.fields.length === 0 ? {} : { extensions: { fields: created.fields } }),
60
55
  });
@@ -72,15 +67,20 @@ export function projectRoutes(deps) {
72
67
  const channels = projectChannels(deps.db);
73
68
  const uploads = uploadedProjects(deps.db);
74
69
  const aside = setAsideProjects(deps.db);
75
- const settled = uploadedAndSettled(deps.db);
70
+ const settled = settledProjects(deps.db);
76
71
  const holds = projectHolds(deps.db, { standings, settled });
77
72
  const waits = limitWaitsByProject(deps.db);
78
73
  const failures = stepFailuresByProject(deps.db);
74
+ const samples = new Set(Object.values(sampleProjectIds(deps.db)));
75
+ const facts = listingFacts(deps.db);
79
76
  const projects = listProjectHeads(deps.db).map((project) => {
80
77
  const stages = standings.get(project.id) ?? [];
81
78
  const waiting = waits.get(project.id);
82
79
  const holding = holds.holding.get(project.id);
83
80
  const status = settledStatus(derive(stages, project.paused), settled.has(project.id));
81
+ const made = facts.get(project.id);
82
+ const finished = status === "done" || status === "partial";
83
+ const retry = status === "running" ? retryingAt(stages) : undefined;
84
84
  return {
85
85
  ...project,
86
86
  status,
@@ -96,7 +96,18 @@ export function projectRoutes(deps) {
96
96
  ? { stepFailures: failures.get(project.id) ?? [] }
97
97
  : {}),
98
98
  ...(holding === undefined ? {} : { holding }),
99
+ ...(retry === undefined ? {} : { retryAt: retry }),
99
100
  ...(waiting === undefined ? {} : { limitWaits: waiting.map(listingWait) }),
101
+ ...(samples.has(project.id) ? { sample: true } : {}),
102
+ ...(finished && made?.finishedAt !== undefined ? { finishedAt: made.finishedAt } : {}),
103
+ ...(made?.durationSeconds === undefined
104
+ ? {}
105
+ : { durationSeconds: made.durationSeconds }),
106
+ ...(made?.outputs === undefined ? {} : { outputs: made.outputs }),
107
+ ...(status === "partial" && made?.failedSteps !== undefined
108
+ ? { failedSteps: made.failedSteps }
109
+ : {}),
110
+ ...(made?.outdated === true ? { outdated: true } : {}),
100
111
  };
101
112
  });
102
113
  return c.json({ projects });
@@ -106,7 +117,6 @@ export function projectRoutes(deps) {
106
117
  if (project === undefined) {
107
118
  return problem(c, {
108
119
  status: 404,
109
- title: titleOf(404),
110
120
  detail: projectTrashed(deps.db, c.req.valid("param").id)
111
121
  ? "This project is in the trash. Restore it in Settings → Trash to open it again."
112
122
  : "This project no longer exists. Go back to Projects to pick another.",
@@ -152,7 +162,6 @@ export function projectRoutes(deps) {
152
162
  if (channelById(deps.db, channelId) === undefined)
153
163
  return problem(c, {
154
164
  status: 404,
155
- title: titleOf(404),
156
165
  detail: "That channel no longer exists. Reload the page and pick one from the list, or make it again in Channels.",
157
166
  });
158
167
  const live = new Set(listProjectHeads(deps.db).map((project) => project.id));
@@ -172,7 +181,6 @@ export function projectRoutes(deps) {
172
181
  const status = result.reason === "running" ? 409 : 404;
173
182
  return problem(c, {
174
183
  status,
175
- title: titleOf(status),
176
184
  detail: result.reason === "running"
177
185
  ? "This project is still running. Use Cancel run on the project page first, then delete it."
178
186
  : "This project no longer exists. Go back to Projects to pick another.",
@@ -8,14 +8,8 @@ import { formerNames } from "../../slices/library/rename.js";
8
8
  import { listPrompts, promptById } from "../../slices/library/repo.js";
9
9
  import { createPrompt, removePrompt, restorePrompt, updatePrompt, } from "../../slices/library/save.js";
10
10
  import { usedBy } from "../../slices/library/used-by.js";
11
- import { onInvalid, problem, titleOf } from "./problem.js";
12
- const idParam = z.object({
13
- id: z
14
- .string()
15
- .min(1)
16
- .max(64)
17
- .regex(/^[0-9A-Za-z_-]+$/),
18
- });
11
+ import { idParam } from "./ids.js";
12
+ import { onInvalid, problem } from "./problem.js";
19
13
  // History's Restore names the version by its number.
20
14
  export const versionParam = idParam.extend({ version: z.coerce.number().int().positive() });
21
15
  // Shape only. The rules - trimming, emptiness, length, the slot lint - are the slice's,
@@ -25,6 +19,8 @@ const promptBody = z.object({
25
19
  name: z.string(),
26
20
  body: z.string(),
27
21
  });
22
+ // Duplicate names the prompt it copies, whose old keyword names the copy may keep.
23
+ const promptCreateBody = promptBody.extend({ copyOf: z.string().min(1).max(100).optional() });
28
24
  // The return type is inferred so Hono keeps the route types the SPA's client is
29
25
  // generated from; see stagingRoutes.
30
26
  export function promptRoutes(deps) {
@@ -33,8 +29,9 @@ export function promptRoutes(deps) {
33
29
  // Every kind in one list: 04 Prompts filters by tab, and Duplicate needs the body it
34
30
  // is copying.
35
31
  .get("/", (c) => c.json({ prompts: withPhotorealistic(deps.db, listPrompts(deps.db)) }))
36
- .post("/", zValidator("json", promptBody, onInvalid), (c) => {
37
- const result = createPrompt(library, c.req.valid("json"));
32
+ .post("/", zValidator("json", promptCreateBody, onInvalid), (c) => {
33
+ const { copyOf, ...draft } = c.req.valid("json");
34
+ const result = createPrompt(library, draft, copyOf);
38
35
  return result.ok ? c.json(result.value, 201) : refused(c, result, "prompt");
39
36
  })
40
37
  .put("/:id", zValidator("param", idParam, onInvalid), zValidator("json", promptBody, onInvalid), (c) => {
@@ -50,8 +47,7 @@ export function promptRoutes(deps) {
50
47
  ? refused(c, { ok: false, reason: "not-found" }, "prompt")
51
48
  : problem(c, {
52
49
  status: 400,
53
- title: titleOf(400),
54
- detail: "Only an Image prompt can be marked photorealistic. Open the prompt from the Image tab in Library → Prompts.",
50
+ detail: "Only an Image prompt can be marked photorealistic. Open the prompt under Image in Prompts.",
55
51
  });
56
52
  })
57
53
  // Every saved version, newest first (`slices/library/history.ts`).
@@ -87,7 +83,6 @@ export function refused(c, failure, noun) {
87
83
  case "invalid":
88
84
  return problem(c, {
89
85
  status: 400,
90
- title: titleOf(400),
91
86
  detail: `This ${noun} cannot be saved yet. Fix the highlighted fields and try again.`,
92
87
  extensions: { fields: failure.fields },
93
88
  });
@@ -95,7 +90,6 @@ export function refused(c, failure, noun) {
95
90
  case "duplicate-name":
96
91
  return problem(c, {
97
92
  status: 409,
98
- title: titleOf(409),
99
93
  detail: `Another ${noun} already has this name. Choose a different name.`,
100
94
  extensions: {
101
95
  fields: [
@@ -109,8 +103,7 @@ export function refused(c, failure, noun) {
109
103
  case "not-found":
110
104
  return problem(c, {
111
105
  status: 404,
112
- title: titleOf(404),
113
- detail: `This ${noun} no longer exists; it may have been deleted. Go back to the Library to pick another.`,
106
+ detail: `This ${noun} no longer exists; it may have been deleted. Go back to Prompts to pick another.`,
114
107
  });
115
108
  }
116
109
  }
@@ -4,7 +4,7 @@ import { z } from "zod";
4
4
  import { aliasCountMax } from "../../kernel/ports/narration-aliases.js";
5
5
  import { listNarrationAliases, saveNarrationAliases, } from "../../slices/narration/aliases-library.js";
6
6
  import { collectSharedGlossary } from "../../slices/narration/shared-glossary.js";
7
- import { onInvalid, problem, titleOf } from "./problem.js";
7
+ import { onInvalid, problem } from "./problem.js";
8
8
  // The pronunciations of every project's glossary, merged, for Edit project to copy into a
9
9
  // project that shares them. `except` leaves out the project being edited, whose own glossary
10
10
  // already comes first. Library → Aliases lives here too: the other way to change how a word
@@ -28,7 +28,6 @@ export function pronunciationRoutes(deps) {
28
28
  return c.json({ aliases: result.aliases });
29
29
  return problem(c, {
30
30
  status: 400,
31
- title: titleOf(400),
32
31
  detail: `These aliases can't be saved yet: ${result.fields.map((field) => field.message).join(" ")}`,
33
32
  extensions: { fields: result.fields },
34
33
  });
@@ -5,11 +5,11 @@ import { z } from "zod";
5
5
  import { keyProbes } from "../../adapters/key-probes.js";
6
6
  import { catalogueModelInfo } from "../../catalog/model-info.js";
7
7
  import { videoModelsOf } from "../../catalog/schema.js";
8
- import { defaultSpeechVoice, detectSpeechCached } from "../../kernel/ports/system-speech.js";
8
+ import { defaultSpeechVoice, detectSpeechCached, speechInstall, } from "../../kernel/ports/system-speech.js";
9
9
  import { switchAllRetired, switchRetiredModel } from "../../slices/model-upkeep/switch.js";
10
10
  import { retiredModelUsage, usageKinds, usageSlots } from "../../slices/model-upkeep/usage.js";
11
11
  import { cliPathMaxLength, saveCliPath } from "../../slices/settings/cli-paths.js";
12
- import { dismissFirstRun, firstRunStatus } from "../../slices/settings/first-run.js";
12
+ import { firstRunStatus } from "../../slices/settings/first-run.js";
13
13
  import { checkProviderHealth } from "../../slices/settings/health.js";
14
14
  import { keyGuides } from "../../slices/settings/key-guides.js";
15
15
  import { testProviderKey } from "../../slices/settings/key-test.js";
@@ -17,7 +17,7 @@ import { keyStatus, removeProviderKey, saveProviderKey } from "../../slices/sett
17
17
  import { isUncataloguedProvider, providerById, providerIds } from "../../slices/settings/model.js";
18
18
  import { createModelCatalog } from "../../slices/settings/models.js";
19
19
  import { providerStatuses } from "../../slices/settings/readiness.js";
20
- import { onInvalid, problem, titleOf } from "./problem.js";
20
+ import { onInvalid, problem } from "./problem.js";
21
21
  const providerParam = z.object({ id: z.enum(providerIds) });
22
22
  const healthQuery = z.object({ provider: z.enum(providerIds).optional() });
23
23
  // ceiling: no format check is allowed on a key, so the only thing said about the value is
@@ -26,7 +26,7 @@ const healthQuery = z.object({ provider: z.enum(providerIds).optional() });
26
26
  const pathBody = z.object({ path: z.string().max(cliPathMaxLength) });
27
27
  const keyBody = z.object({ key: z.string().min(1).max(4096) });
28
28
  const modelId = z.string().min(1).max(200);
29
- const switchBody = z
29
+ export const switchBody = z
30
30
  .object({
31
31
  kind: z.enum(usageKinds),
32
32
  id: z.string().min(1).max(64),
@@ -53,24 +53,29 @@ export function providerRoutes(deps) {
53
53
  report: (provider) => deps.log.write("warn", "model-catalog", { detail: `Could not load models for ${provider}` }),
54
54
  });
55
55
  const upkeep = () => ({ ...deps, uuid: deps.drafts?.uuid ?? randomUUID });
56
+ // A picker opening reads the key's own model list again when it is due; never waited for.
57
+ const relist = (id) => {
58
+ if (deps.autoModelChecks !== true)
59
+ return;
60
+ void deps.modelListing?.refresh(id).catch((error) => {
61
+ deps.log.write("warn", "model-checks.listing", { detail: `${id}: ${String(error)}` });
62
+ });
63
+ };
64
+ const standingsOf = (id, models) => deps.modelChecks?.standings(id, models);
56
65
  return (new Hono()
57
- // The step lists Settings → Providers shows beside each key field.
66
+ // The step lists Settings → Keys & services shows beside each key field.
58
67
  .get("/key-guides", (c) => c.json({ guides: keyGuides }))
59
68
  // What the first launch found, and the providers Play picks by default.
60
69
  .get("/first-run", async (c) => {
61
70
  c.header("Cache-Control", "no-store");
62
71
  return c.json(await firstRunStatus({ db: deps.db, ...(deps.modelsFor === undefined ? {} : { modelsFor: deps.modelsFor }) }, await providerStatuses(readiness)));
63
- })
64
- .post("/first-run/dismiss", (c) => {
65
- dismissFirstRun(deps.db);
66
- return c.body(null, 204);
67
72
  })
68
73
  // "Check all": every CLI signed in, every key valid, every chosen model still offered and
69
74
  // answering for the key. `?provider=codex` checks that one only (its row's Check again,
70
75
  // or a sign-in fix-it's).
71
76
  .post("/health", zValidator("query", healthQuery, onInvalid), async (c) => {
72
77
  if (deps.fetch === undefined)
73
- return problem(c, { status: 503, title: titleOf(503), detail: noFetch });
78
+ return problem(c, { status: 503, detail: noFetch });
74
79
  const catalogue = deps.catalogue;
75
80
  return c.json(await checkProviderHealth({
76
81
  ...readiness,
@@ -87,7 +92,6 @@ export function providerRoutes(deps) {
87
92
  if (!deps.catalogue?.sync)
88
93
  return problem(c, {
89
94
  status: 503,
90
- title: titleOf(503),
91
95
  detail: "The model list (models.yaml) is not loaded yet. Wait a moment and reload the page; if it stays like this, restart Slopify.",
92
96
  });
93
97
  await deps.catalogue.sync();
@@ -97,21 +101,23 @@ export function providerRoutes(deps) {
97
101
  .get("/catalogue/retired", (c) => {
98
102
  c.header("Cache-Control", "no-store");
99
103
  return c.json({
100
- usages: deps.catalogue === undefined ? [] : retiredModelUsage(deps.db, deps.catalogue.read()),
104
+ usages: deps.catalogue === undefined
105
+ ? []
106
+ : retiredModelUsage(deps.db, deps.catalogue.read(), deps.catalogue.lifecycle?.()),
101
107
  });
102
108
  })
103
109
  .post("/catalogue/retired/switch", zValidator("json", switchBody, onInvalid), async (c) => {
104
110
  if (deps.catalogue === undefined)
105
- return problem(c, { status: 503, title: titleOf(503), detail: noFetch });
111
+ return problem(c, { status: 503, detail: noFetch });
106
112
  const result = await switchRetiredModel(upkeep(), deps.catalogue.read(), c.req.valid("json"));
107
113
  if (!result.ok)
108
- return problem(c, { status: 409, title: titleOf(409), detail: result.message });
114
+ return problem(c, { status: 409, detail: result.message });
109
115
  return c.json(result);
110
116
  })
111
117
  .post("/catalogue/retired/switch-all", async (c) => {
112
118
  if (deps.catalogue === undefined)
113
- return problem(c, { status: 503, title: titleOf(503), detail: noFetch });
114
- return c.json(await switchAllRetired(upkeep(), deps.catalogue.read()));
119
+ return problem(c, { status: 503, detail: noFetch });
120
+ return c.json(await switchAllRetired(upkeep(), deps.catalogue.read(), deps.catalogue.lifecycle?.()));
115
121
  })
116
122
  .get("/catalogue", (c) => c.json(deps.catalogue?.status() ?? {
117
123
  warning: "The model list (models.yaml) is not loaded yet. Wait a moment and reload the page; if it stays like this, restart Slopify.",
@@ -120,7 +126,6 @@ export function providerRoutes(deps) {
120
126
  if (!deps.catalogue)
121
127
  return problem(c, {
122
128
  status: 503,
123
- title: titleOf(503),
124
129
  detail: "The model list (models.yaml) is not loaded yet. Wait a moment and reload the page; if it stays like this, restart Slopify.",
125
130
  });
126
131
  try {
@@ -129,7 +134,6 @@ export function providerRoutes(deps) {
129
134
  catch {
130
135
  return problem(c, {
131
136
  status: 502,
132
- title: titleOf(502),
133
137
  detail: "Slopify could not download the latest model list. Your current list is still in use; check your internet connection and try Refresh again.",
134
138
  });
135
139
  }
@@ -138,9 +142,11 @@ export function providerRoutes(deps) {
138
142
  // What Settings draws its rails from and Play its dropdowns: every provider, with
139
143
  // the one fact that decides whether it is selectable.
140
144
  .get("/", async (c) => c.json({ providers: await providerStatuses(readiness) }))
141
- // The system voice's speech programs and their voices, for Settings → Voices' picker.
145
+ // The system voice's speech programs and their voices, for Settings → Voices' picker, and
146
+ // with none, how to install one from a terminal (the commands apart from the sentence).
142
147
  .get("/system-voice/voices", async (c) => {
143
- const found = await detectSpeechCached(deps.probe, deps.speechHost ?? process);
148
+ const host = deps.speechHost ?? process;
149
+ const found = await detectSpeechCached(deps.probe, host);
144
150
  return c.json({
145
151
  engines: found.engines.map((engine) => ({
146
152
  id: engine.id,
@@ -149,36 +155,43 @@ export function providerRoutes(deps) {
149
155
  defaultVoice: defaultSpeechVoice(engine)?.id ?? null,
150
156
  })),
151
157
  issue: found.issue ?? null,
158
+ install: found.engines.length === 0 ? (speechInstall(host) ?? null) : null,
152
159
  });
153
160
  })
154
161
  .get("/:id/models", zValidator("param", providerParam, onInvalid), async (c) => {
155
162
  const { id } = c.req.valid("param");
156
163
  // Animate images lists the provider's image-to-video models, which only the catalogue
157
164
  // names.
158
- if (c.req.query("video") === "1")
165
+ relist(id);
166
+ if (c.req.query("video") === "1") {
167
+ const videos = deps.catalogue === undefined ? [] : videoModelsOf(deps.catalogue.read(), id);
159
168
  return c.json({
160
- models: deps.catalogue === undefined ? [] : videoModelsOf(deps.catalogue.read(), id),
169
+ models: videos,
161
170
  allowsCustom: false,
171
+ standings: standingsOf(id, videos),
162
172
  });
163
- if (deps.catalogue && !isUncataloguedProvider(id))
173
+ }
174
+ if (deps.catalogue && !isUncataloguedProvider(id)) {
175
+ const models = deps.catalogue
176
+ .models(id, providerById(id).family)
177
+ .map((m) => ({ ...m, ...catalogueModelInfo(m) }));
164
178
  return c.json({
165
- models: deps.catalogue
166
- .models(id, providerById(id).family)
167
- .map((m) => ({ ...m, ...catalogueModelInfo(m) })),
179
+ models,
168
180
  allowsCustom: false,
169
181
  notice: "Curated active models from models.yaml. Edit it locally or refresh in Settings.",
170
182
  warning: deps.catalogue.status().warning ?? undefined,
183
+ standings: standingsOf(id, models),
171
184
  });
185
+ }
172
186
  const result = await catalog.get(id, providerById(id).family, c.req.query("refresh") === "1");
173
187
  c.header("Cache-Control", "no-store");
174
- return c.json(result);
188
+ return c.json({ ...result, standings: standingsOf(id, result.models) });
175
189
  })
176
190
  .put("/:id/path", zValidator("param", providerParam, onInvalid), zValidator("json", pathBody, onInvalid), async (c) => {
177
191
  const result = await saveCliPath(readiness, c.req.valid("param").id, c.req.valid("json").path);
178
192
  if (!result.ok)
179
193
  return problem(c, {
180
194
  status: 400,
181
- title: titleOf(400),
182
195
  detail: result.message,
183
196
  extensions: { fields: [{ field: "path", message: result.message }] },
184
197
  });
@@ -192,6 +205,11 @@ export function providerRoutes(deps) {
192
205
  return refusal(c, id, result.reason);
193
206
  }
194
207
  catalog.invalidate(id);
208
+ // A new key: read its model list and check what is free to check, in the background.
209
+ if (deps.autoModelChecks === true)
210
+ void deps.modelChecks?.automatic("key-added", id).catch((error) => {
211
+ deps.log.write("warn", "model-checks", { detail: `${id}: ${String(error)}` });
212
+ });
195
213
  // The response says a key is stored and shows the mask, never the value.
196
214
  return c.json(keyStatus(keys, id));
197
215
  })
@@ -203,14 +221,13 @@ export function providerRoutes(deps) {
203
221
  if (providerById(id).auth !== "key")
204
222
  return refusal(c, id, "cli-provider");
205
223
  if (deps.fetch === undefined)
206
- return problem(c, { status: 503, title: titleOf(503), detail: noFetch });
224
+ return problem(c, { status: 503, detail: noFetch });
207
225
  const raw = await c.req.json().catch(() => undefined);
208
226
  const candidate = raw === undefined ? undefined : keyBody.safeParse(raw);
209
227
  if (candidate !== undefined && !candidate.success)
210
228
  return problem(c, {
211
229
  status: 400,
212
- title: titleOf(400),
213
- detail: `The pasted ${providerById(id).displayName} key could not be tested: it is empty or longer than any key. Paste the whole key in Settings → Providers → ${providerById(id).displayName}, then choose Test again.`,
230
+ detail: `The pasted ${providerById(id).displayName} key could not be tested: it is empty or longer than any key. Paste the whole key in Settings → Keys & services → ${providerById(id).displayName}, then choose Test again.`,
214
231
  });
215
232
  return c.json(await testProviderKey({ db: deps.db, clock: deps.clock, fetch: deps.fetch, probes: keyProbes }, id, candidate?.data.key));
216
233
  })
@@ -229,7 +246,6 @@ function refusal(c, id, reason) {
229
246
  if (reason === "cli-provider") {
230
247
  return problem(c, {
231
248
  status: 400,
232
- title: titleOf(400),
233
249
  detail: providerById(id).auth === "local"
234
250
  ? `${name} is your computer's own speech, so it has no API key to save. It works as soon as a speech program is installed.`
235
251
  : `${name} signs in through its own command-line tool, so it has no API key to save here. Sign in with that tool on your computer instead.`,
@@ -238,13 +254,11 @@ function refusal(c, id, reason) {
238
254
  if (reason === "absent") {
239
255
  return problem(c, {
240
256
  status: 404,
241
- title: titleOf(404),
242
257
  detail: `There is no saved API key for ${name}, so there is nothing to remove.`,
243
258
  });
244
259
  }
245
260
  return problem(c, {
246
261
  status: 400,
247
- title: titleOf(400),
248
262
  detail: "This API key cannot be saved yet. Paste the key and try again.",
249
263
  extensions: {
250
264
  fields: [{ field: "key", message: "Paste the API key from your provider's account page." }],
@@ -0,0 +1,29 @@
1
+ import { problem } from "./problem.js";
2
+ export function refusal(c, reason, rules) {
3
+ return problem(c, {
4
+ status: rules.statuses[reason] ?? rules.otherwise,
5
+ detail: rules.detail,
6
+ extensions: { reason, ...rules.extensions },
7
+ });
8
+ }
9
+ export function revisionRequired(c) {
10
+ return problem(c, {
11
+ status: 409,
12
+ detail: "This change is now made in Edit settings: save it there, then use Choose what to make again (in the project's More menu).",
13
+ extensions: { reason: "revision-required" },
14
+ });
15
+ }
16
+ // A build without its bundled files says so in plain words; anything else is the app's error handler's.
17
+ export function bundleMissing(c, error, Missing) {
18
+ if (!(error instanceof Missing))
19
+ throw error;
20
+ return problem(c, { status: 500, detail: error.message });
21
+ }
22
+ export async function jsonBody(c) {
23
+ try {
24
+ return await c.req.json();
25
+ }
26
+ catch {
27
+ return undefined;
28
+ }
29
+ }
@@ -0,0 +1,40 @@
1
+ import { zValidator } from "@hono/zod-validator";
2
+ import { Hono } from "hono";
3
+ import { z } from "zod";
4
+ import { actorOf } from "./actor.js";
5
+ import { onInvalid, problem } from "./problem.js";
6
+ import { viaProxy } from "./same-page.js";
7
+ const body = z.object({
8
+ enabled: z.boolean(),
9
+ httpsPort: z.number().int().min(1).max(65535).optional(),
10
+ });
11
+ // Settings → Remote access: Slopify's page on the person's other devices through
12
+ // Tailscale serve. Reading it is open to agents (get_remote_access); turning it on or off is
13
+ // the person's alone, from this computer.
14
+ export function remoteAccessRoutes(service) {
15
+ const unavailable = "Remote access can't be set from this Slopify. Start it with npx @gentbajko/slopify and try Settings → Remote access again.";
16
+ return new Hono()
17
+ .get("/", async (c) => {
18
+ if (service === undefined)
19
+ return problem(c, { status: 404, detail: unavailable });
20
+ c.header("Cache-Control", "no-store");
21
+ const view = await service.view();
22
+ return c.json(viaProxy(c.req.raw.headers) ? { ...view, changeable: false } : view);
23
+ })
24
+ .put("/", zValidator("json", body, onInvalid), async (c) => {
25
+ if (service === undefined)
26
+ return problem(c, { status: 404, detail: unavailable });
27
+ if (actorOf(c) !== "person")
28
+ return problem(c, {
29
+ status: 403,
30
+ detail: "Remote access is the person's to turn on or off, in Settings → Remote access. Agents and the assistant can only read it (get_remote_access).",
31
+ });
32
+ if (viaProxy(c.req.raw.headers))
33
+ return problem(c, {
34
+ status: 403,
35
+ detail: "Remote access is turned on or off only on the computer Slopify runs on. Open Settings → Remote access there, at http://127.0.0.1 and Slopify's port.",
36
+ });
37
+ const { enabled, httpsPort } = c.req.valid("json");
38
+ return c.json(await service.set(enabled, httpsPort));
39
+ });
40
+ }