@gentbajko/slopify 3.25.9 → 4.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (603) hide show
  1. package/dist/adapters/account-limits.js +30 -0
  2. package/dist/adapters/alignment/cache.js +3 -5
  3. package/dist/adapters/alignment/ctc.js +1 -2
  4. package/dist/adapters/alignment/lock.js +4 -3
  5. package/dist/adapters/alignment/multilingual.js +0 -3
  6. package/dist/adapters/alignment/prefetch.js +2 -1
  7. package/dist/adapters/alignment/worker.js +2 -1
  8. package/dist/adapters/explain.js +0 -3
  9. package/dist/adapters/ffmpeg.js +19 -1
  10. package/dist/adapters/host-cli/index.js +19 -3
  11. package/dist/adapters/http-failure.js +53 -0
  12. package/dist/adapters/image/codex.js +36 -4
  13. package/dist/adapters/image/fal.js +17 -50
  14. package/dist/adapters/image/google.js +31 -55
  15. package/dist/adapters/image/openai.js +20 -47
  16. package/dist/adapters/image/reference.js +36 -0
  17. package/dist/adapters/image/replicate.js +18 -47
  18. package/dist/adapters/interest/search-interest.js +113 -0
  19. package/dist/adapters/key-probes.js +53 -2
  20. package/dist/adapters/llm/antigravity.js +4 -0
  21. package/dist/adapters/llm/assistant-cli.js +388 -0
  22. package/dist/adapters/llm/child-session.js +83 -0
  23. package/dist/adapters/llm/claude-code-models.js +51 -98
  24. package/dist/adapters/llm/claude-code.js +13 -2
  25. package/dist/adapters/llm/codex-limits.js +27 -68
  26. package/dist/adapters/llm/codex-server-models.js +61 -109
  27. package/dist/adapters/llm/codex.js +12 -1
  28. package/dist/adapters/llm/openrouter.js +10 -47
  29. package/dist/adapters/llm/run-cli.js +6 -22
  30. package/dist/adapters/llm/sse-lines.js +5 -1
  31. package/dist/adapters/model-access.js +37 -0
  32. package/dist/adapters/tts/cartesia.js +15 -46
  33. package/dist/adapters/tts/elevenlabs.js +20 -52
  34. package/dist/adapters/tts/gemini.js +19 -40
  35. package/dist/adapters/tts/inworld-async.js +4 -1
  36. package/dist/adapters/tts/inworld.js +3 -0
  37. package/dist/adapters/tts/openai.js +20 -50
  38. package/dist/assets/models-lifecycle.yaml +6 -0
  39. package/dist/assistant-wiring.js +60 -0
  40. package/dist/autopilot-timers.js +59 -0
  41. package/dist/catalog/lifecycle.js +52 -0
  42. package/dist/catalog/propose.js +204 -0
  43. package/dist/catalog/registry.js +3 -1
  44. package/dist/catalog/schema.js +2 -0
  45. package/dist/catalog/store.js +39 -0
  46. package/dist/edge/autostart/native.js +3 -5
  47. package/dist/edge/autostart/prompt.js +2 -1
  48. package/dist/edge/autostart/service.js +3 -4
  49. package/dist/edge/cli-args.js +3 -4
  50. package/dist/edge/cli.js +2 -1
  51. package/dist/edge/docker-install/apply.js +11 -11
  52. package/dist/edge/docker-install/run.js +2 -1
  53. package/dist/edge/docker-install/state.js +4 -3
  54. package/dist/edge/docker-install/tree.js +2 -4
  55. package/dist/edge/docker-install/volume.js +3 -2
  56. package/dist/edge/docker.js +5 -4
  57. package/dist/edge/events/hub.js +5 -5
  58. package/dist/edge/http/actions.js +4 -25
  59. package/dist/edge/http/actor.js +5 -3
  60. package/dist/edge/http/app.js +85 -10
  61. package/dist/edge/http/assistant.js +127 -0
  62. package/dist/edge/http/audio-preview.js +4 -11
  63. package/dist/edge/http/auditions.js +1 -1
  64. package/dist/edge/http/autostart.js +5 -5
  65. package/dist/edge/http/backups.js +1 -3
  66. package/dist/edge/http/channel-memory.js +7 -20
  67. package/dist/edge/http/channels.js +13 -26
  68. package/dist/edge/http/checkpoints.js +33 -17
  69. package/dist/edge/http/design-templates.js +167 -0
  70. package/dist/edge/http/designs.js +178 -0
  71. package/dist/edge/http/diagnostics.js +5 -1
  72. package/dist/edge/http/document-themes.js +2 -11
  73. package/dist/edge/http/draft-files.js +1 -3
  74. package/dist/edge/http/draft-problem.js +4 -9
  75. package/dist/edge/http/drafts.js +3 -3
  76. package/dist/edge/http/entries.js +6 -9
  77. package/dist/edge/http/extension-auth.js +27 -0
  78. package/dist/edge/http/external-reviews.js +12 -11
  79. package/dist/edge/http/files.js +5 -11
  80. package/dist/edge/http/folder-location.js +1 -4
  81. package/dist/edge/http/fonts.js +0 -7
  82. package/dist/edge/http/home.js +38 -13
  83. package/dist/edge/http/host-cli.js +18 -8
  84. package/dist/edge/http/ids.js +8 -0
  85. package/dist/edge/http/keywords.js +35 -0
  86. package/dist/edge/http/model-checks.js +132 -0
  87. package/dist/edge/http/narration-peaks.js +5 -13
  88. package/dist/edge/http/onboarding.js +1 -14
  89. package/dist/edge/http/open-folder.js +5 -10
  90. package/dist/edge/http/outputs.js +4 -12
  91. package/dist/edge/http/patch-notes.js +4 -10
  92. package/dist/edge/http/planning.js +2 -6
  93. package/dist/edge/http/problem.js +2 -4
  94. package/dist/edge/http/project-create.js +1 -0
  95. package/dist/edge/http/project-templates.js +29 -17
  96. package/dist/edge/http/projects.js +2 -12
  97. package/dist/edge/http/prompts.js +7 -14
  98. package/dist/edge/http/pronunciations.js +1 -2
  99. package/dist/edge/http/providers.js +39 -24
  100. package/dist/edge/http/refusal.js +29 -0
  101. package/dist/edge/http/remote-access.js +40 -0
  102. package/dist/edge/http/remote-guard.js +71 -0
  103. package/dist/edge/http/request-guard.js +91 -0
  104. package/dist/edge/http/reviews.js +11 -24
  105. package/dist/edge/http/revision-files.js +5 -10
  106. package/dist/edge/http/revisions.js +10 -15
  107. package/dist/edge/http/run-cost.js +1 -2
  108. package/dist/edge/http/same-page.js +80 -0
  109. package/dist/edge/http/schedules.js +59 -32
  110. package/dist/edge/http/settings.js +22 -13
  111. package/dist/edge/http/social.js +282 -0
  112. package/dist/edge/http/staging.js +2 -4
  113. package/dist/edge/http/storage-files.js +5 -7
  114. package/dist/edge/http/storage.js +23 -23
  115. package/dist/edge/http/studio.js +23 -50
  116. package/dist/edge/http/style-preview.js +1 -4
  117. package/dist/edge/http/subtitles.js +3 -17
  118. package/dist/edge/http/template-studio.js +433 -0
  119. package/dist/edge/http/trash.js +10 -11
  120. package/dist/edge/http/tutorial.js +1 -2
  121. package/dist/edge/http/tutorials.js +5 -11
  122. package/dist/edge/http/update.js +7 -13
  123. package/dist/edge/http/waveform.js +3 -6
  124. package/dist/edge/http/youtube-edits.js +2 -3
  125. package/dist/edge/mcp/assistant.js +279 -0
  126. package/dist/edge/mcp/attribution.js +8 -2
  127. package/dist/edge/mcp/call-route.js +11 -2
  128. package/dist/edge/mcp/excluded-routes.js +25 -0
  129. package/dist/edge/mcp/http.js +11 -2
  130. package/dist/edge/mcp/instructions.js +5 -3
  131. package/dist/edge/mcp/library-tools.js +1 -0
  132. package/dist/edge/mcp/route-tool.js +35 -15
  133. package/dist/edge/mcp/server.js +13 -8
  134. package/dist/edge/mcp/tools-channels.js +2 -0
  135. package/dist/edge/mcp/tools-designs.js +165 -0
  136. package/dist/edge/mcp/tools-library.js +14 -2
  137. package/dist/edge/mcp/tools-planning.js +23 -4
  138. package/dist/edge/mcp/tools-projects.js +28 -1
  139. package/dist/edge/mcp/tools-runs.js +14 -1
  140. package/dist/edge/mcp/tools-schedules.js +28 -4
  141. package/dist/edge/mcp/tools-setup.js +61 -3
  142. package/dist/edge/mcp/tools-social.js +46 -0
  143. package/dist/edge/mcp/tools-storage.js +8 -0
  144. package/dist/edge/mcp/tools-template-studio.js +166 -0
  145. package/dist/edge/mcp/tools.js +6 -0
  146. package/dist/edge/remote-access/cli.js +71 -0
  147. package/dist/edge/remote-access/model.js +32 -0
  148. package/dist/edge/remote-access/rules.js +207 -0
  149. package/dist/edge/remote-access/service.js +203 -0
  150. package/dist/edge/remote-access/store.js +44 -0
  151. package/dist/edge/remote-access/views.js +43 -0
  152. package/dist/extension/slopify-studio-chrome.zip +0 -0
  153. package/dist/extension/slopify-studio-firefox.zip +0 -0
  154. package/dist/host-cli/install.js +2 -2
  155. package/dist/host-cli/open-folder.js +2 -2
  156. package/dist/host-cli/paths.js +2 -4
  157. package/dist/host-cli/server.js +4 -4
  158. package/dist/host-cli/service.js +3 -3
  159. package/dist/host-cli/status.js +7 -7
  160. package/dist/kernel/db/migrations/0063-model-checks.sql +20 -0
  161. package/dist/kernel/db/migrations/0064-template-studio.sql +46 -0
  162. package/dist/kernel/db/migrations/0065-promise-and-assistant-reviews.sql +58 -0
  163. package/dist/kernel/db/migrations/0066-assistant.sql +44 -0
  164. package/dist/kernel/db/migrations/0067-autopilot.sql +6 -0
  165. package/dist/kernel/db/migrations/0068-topic-scores.sql +17 -0
  166. package/dist/kernel/db/migrations/0070-designs.sql +53 -0
  167. package/dist/kernel/db/migrations/0071-design-usage.sql +33 -0
  168. package/dist/kernel/db/migrations/0072-design-templates.sql +33 -0
  169. package/dist/kernel/db/migrations/0073-schedule-design-kind.sql +5 -0
  170. package/dist/kernel/db/migrations/0074-template-bakeoffs.sql +44 -0
  171. package/dist/kernel/db/migrations/0075-social-posts.sql +25 -0
  172. package/dist/kernel/db/migrations/0076-channel-social.sql +5 -0
  173. package/dist/kernel/db/migrations/0077-post-prompts.sql +17 -0
  174. package/dist/kernel/db/tx.js +2 -4
  175. package/dist/kernel/errors.js +27 -1
  176. package/dist/kernel/json-answer.js +15 -0
  177. package/dist/kernel/lock.js +4 -9
  178. package/dist/kernel/paths.js +8 -0
  179. package/dist/kernel/ports/assistant-cli.js +4 -0
  180. package/dist/kernel/ports/host-cli.js +3 -0
  181. package/dist/kernel/ports/interest.js +4 -0
  182. package/dist/kernel/ports/languages.js +0 -3
  183. package/dist/kernel/ports/model.js +2 -0
  184. package/dist/kernel/runner/attempt.js +3 -1
  185. package/dist/kernel/runner/audition.js +3 -1
  186. package/dist/kernel/runner/checkpoint-authority.js +2 -1
  187. package/dist/kernel/runner/index.js +8 -10
  188. package/dist/kernel/runner/model-access.js +40 -0
  189. package/dist/kernel/runner/model-check.js +153 -0
  190. package/dist/kernel/runner/providers.js +15 -10
  191. package/dist/kernel/runner/standalone.js +9 -5
  192. package/dist/main.js +334 -19
  193. package/dist/model-checks.js +166 -0
  194. package/dist/patch-notes/4.0.0.md +51 -0
  195. package/dist/patch-notes/index.json +6 -0
  196. package/dist/slices/admission/heads.js +2 -1
  197. package/dist/slices/admission/repo.js +2 -1
  198. package/dist/slices/admission/rules.js +43 -8
  199. package/dist/slices/admission/schema.js +8 -0
  200. package/dist/slices/admission/stage-writes.js +0 -3
  201. package/dist/slices/admission/start.js +48 -31
  202. package/dist/slices/admission/substitute.js +7 -4
  203. package/dist/slices/agents/attention-posts.js +31 -0
  204. package/dist/slices/agents/attention-templates.js +33 -0
  205. package/dist/slices/agents/attention.js +10 -1
  206. package/dist/slices/agents/connection.js +2 -6
  207. package/dist/slices/agents/model.js +6 -3
  208. package/dist/slices/agents/repo.js +15 -7
  209. package/dist/slices/article/continuation.js +0 -63
  210. package/dist/slices/article/plain.js +3 -2
  211. package/dist/slices/article/segments.js +2 -62
  212. package/dist/slices/assistant/access.js +1 -0
  213. package/dist/slices/assistant/asks.js +80 -0
  214. package/dist/slices/assistant/cost.js +54 -0
  215. package/dist/slices/assistant/gate.js +222 -0
  216. package/dist/slices/assistant/model.js +79 -0
  217. package/dist/slices/assistant/names.js +23 -0
  218. package/dist/slices/assistant/prompt.js +74 -0
  219. package/dist/slices/assistant/repo.js +74 -0
  220. package/dist/slices/assistant/tokens.js +18 -0
  221. package/dist/slices/assistant/turn.js +342 -0
  222. package/dist/slices/autopilot/buffer.js +76 -0
  223. package/dist/slices/autopilot/model.js +14 -0
  224. package/dist/slices/autopilot/notify.js +27 -0
  225. package/dist/slices/autopilot/repo.js +34 -0
  226. package/dist/slices/autopilot/signals.js +194 -0
  227. package/dist/slices/autopilot/warnings.js +85 -0
  228. package/dist/slices/autopilot/weekly.js +100 -0
  229. package/dist/slices/backups/files.js +2 -6
  230. package/dist/slices/backups/repo.js +3 -15
  231. package/dist/slices/backups/service.js +4 -3
  232. package/dist/slices/channels/cast-images.js +2 -1
  233. package/dist/slices/channels/model.js +0 -1
  234. package/dist/slices/channels/rebrand.js +0 -3
  235. package/dist/slices/channels/repo.js +4 -0
  236. package/dist/slices/channels/schema.js +6 -0
  237. package/dist/slices/channels/service.js +10 -0
  238. package/dist/slices/channels/videos.js +2 -14
  239. package/dist/slices/checkpoints/index.js +0 -1
  240. package/dist/slices/control/providers.js +0 -26
  241. package/dist/slices/designs/artwork.js +66 -0
  242. package/dist/slices/designs/budget.js +23 -0
  243. package/dist/slices/designs/calls.js +124 -0
  244. package/dist/slices/designs/check-context.js +38 -0
  245. package/dist/slices/designs/check-text.js +43 -0
  246. package/dist/slices/designs/fields.js +88 -0
  247. package/dist/slices/designs/frames.js +34 -0
  248. package/dist/slices/designs/gate.js +128 -0
  249. package/dist/slices/designs/layout.js +293 -0
  250. package/dist/slices/designs/model.js +35 -0
  251. package/dist/slices/designs/pdf-job.js +23 -0
  252. package/dist/slices/designs/pdf-pages.js +30 -0
  253. package/dist/slices/designs/pdf-runner.js +68 -0
  254. package/dist/slices/designs/pdf-worker.js +24 -0
  255. package/dist/slices/designs/picture-size.js +79 -0
  256. package/dist/slices/designs/pictures.js +55 -0
  257. package/dist/slices/designs/quote.js +24 -0
  258. package/dist/slices/designs/render-args.js +36 -0
  259. package/dist/slices/designs/render-input.js +59 -0
  260. package/dist/slices/designs/render.js +215 -0
  261. package/dist/slices/designs/repo.js +101 -0
  262. package/dist/slices/designs/requests.js +80 -0
  263. package/dist/slices/designs/runner.js +239 -0
  264. package/dist/slices/designs/sample-repo.js +91 -0
  265. package/dist/slices/designs/sample.js +222 -0
  266. package/dist/slices/designs/schema.js +87 -0
  267. package/dist/slices/designs/sections.js +44 -0
  268. package/dist/slices/designs/service.js +233 -0
  269. package/dist/slices/designs/steps-repo.js +102 -0
  270. package/dist/slices/designs/studio-kind.js +39 -0
  271. package/dist/slices/designs/template-checks.js +44 -0
  272. package/dist/slices/designs/template-make.js +118 -0
  273. package/dist/slices/designs/templates.js +218 -0
  274. package/dist/slices/document/fonts.js +2 -1
  275. package/dist/slices/episodes/summarize.js +5 -1
  276. package/dist/slices/estimate/index.js +45 -7
  277. package/dist/slices/fonts/catalog.js +3 -2
  278. package/dist/slices/fonts/discovery.js +3 -2
  279. package/dist/slices/fonts/files.js +2 -6
  280. package/dist/slices/fonts/index.js +0 -1
  281. package/dist/slices/fonts/sfnt.js +45 -10
  282. package/dist/slices/images/appearance.js +6 -15
  283. package/dist/slices/images/scenes.js +100 -27
  284. package/dist/slices/keywords/digest.js +84 -0
  285. package/dist/slices/keywords/facts.js +92 -0
  286. package/dist/slices/keywords/fill.js +80 -0
  287. package/dist/slices/keywords/model.js +75 -0
  288. package/dist/slices/keywords/rules.js +187 -0
  289. package/dist/slices/keywords/schema.js +28 -0
  290. package/dist/slices/keywords/specs.js +260 -0
  291. package/dist/slices/keywords/values.js +74 -0
  292. package/dist/slices/library/lint.js +29 -12
  293. package/dist/slices/library/model.js +2 -0
  294. package/dist/slices/library/photorealistic.js +4 -12
  295. package/dist/slices/library/rename.js +9 -7
  296. package/dist/slices/library/save.js +14 -10
  297. package/dist/slices/library/slots.js +37 -11
  298. package/dist/slices/library/transfer.js +2 -1
  299. package/dist/slices/library/used-by.js +25 -10
  300. package/dist/slices/loudness/loudnorm.js +51 -11
  301. package/dist/slices/model-checks/cost.js +61 -0
  302. package/dist/slices/model-checks/listing.js +124 -0
  303. package/dist/slices/model-checks/model.js +20 -0
  304. package/dist/slices/model-checks/record.js +32 -0
  305. package/dist/slices/model-checks/repo.js +120 -0
  306. package/dist/slices/model-checks/rules.js +112 -0
  307. package/dist/slices/model-checks/run.js +143 -0
  308. package/dist/slices/model-checks/service.js +198 -0
  309. package/dist/slices/model-upkeep/switch.js +70 -25
  310. package/dist/slices/model-upkeep/usage.js +26 -11
  311. package/dist/slices/narration/breaths.js +2 -1
  312. package/dist/slices/narration/clean-pieces.js +2 -1
  313. package/dist/slices/narration/concat.js +2 -1
  314. package/dist/slices/narration/peaks.js +3 -4
  315. package/dist/slices/narration/pronunciation-chunks.js +4 -3
  316. package/dist/slices/narration/pronunciation.js +3 -2
  317. package/dist/slices/narration/steering.js +4 -3
  318. package/dist/slices/notifications/notifier.js +4 -3
  319. package/dist/slices/notifications/rules.js +5 -2
  320. package/dist/slices/notifications/settings.js +4 -12
  321. package/dist/slices/notifications/subject.js +2 -1
  322. package/dist/slices/onboarding/copy.js +2 -1
  323. package/dist/slices/onboarding/first-run.js +3 -2
  324. package/dist/slices/onboarding/full-video.js +3 -2
  325. package/dist/slices/onboarding/install.js +4 -3
  326. package/dist/slices/onboarding/model.js +0 -16
  327. package/dist/slices/onboarding/packs.js +22 -22
  328. package/dist/slices/onboarding/quick-short.js +2 -1
  329. package/dist/slices/onboarding/sample.js +0 -1
  330. package/dist/slices/onboarding/state.js +2 -11
  331. package/dist/slices/onboarding/template.js +2 -1
  332. package/dist/slices/patch-notes/library.js +3 -2
  333. package/dist/slices/play-drafts/convert.js +4 -0
  334. package/dist/slices/play-drafts/review-inputs.js +8 -2
  335. package/dist/slices/play-drafts/review.js +5 -4
  336. package/dist/slices/play-drafts/schema.js +4 -0
  337. package/dist/slices/project-templates/from-project.js +9 -2
  338. package/dist/slices/project-templates/repo.js +6 -1
  339. package/dist/slices/project-templates/setup.js +8 -0
  340. package/dist/slices/project-templates/stand-ins.js +22 -0
  341. package/dist/slices/rebuild/activity.js +2 -0
  342. package/dist/slices/rebuild/admission-repo.js +3 -2
  343. package/dist/slices/rebuild/narration-history.js +4 -5
  344. package/dist/slices/rebuild/narration-retry.js +2 -1
  345. package/dist/slices/rebuild/preview-details.js +27 -9
  346. package/dist/slices/rebuild/preview-plan.js +3 -8
  347. package/dist/slices/rebuild/recipe-build.js +8 -3
  348. package/dist/slices/rebuild/recipe-describe.js +3 -3
  349. package/dist/slices/rebuild/recipe-digest.js +58 -0
  350. package/dist/slices/rebuild/recipe-input-schema.js +10 -0
  351. package/dist/slices/rebuild/recipe-keywords.js +71 -0
  352. package/dist/slices/rebuild/recipe-legacy.js +5 -1
  353. package/dist/slices/rebuild/recipe-model.js +4 -0
  354. package/dist/slices/rebuild/recipe-narration-text.js +2 -1
  355. package/dist/slices/rebuild/recipe-preparation.js +3 -3
  356. package/dist/slices/rebuild/recipe-reference.js +8 -4
  357. package/dist/slices/rebuild/recipe-reviews.js +27 -7
  358. package/dist/slices/rebuild/recipe-save.js +6 -3
  359. package/dist/slices/rebuild/recipe-scenes.js +40 -11
  360. package/dist/slices/rebuild/recipe-shorts.js +8 -8
  361. package/dist/slices/rebuild/recipe-social.js +46 -0
  362. package/dist/slices/rebuild/recipe-text.js +64 -20
  363. package/dist/slices/rebuild/recipe-validation.js +98 -28
  364. package/dist/slices/rebuild/recipe-visual.js +55 -16
  365. package/dist/slices/rebuild/recipe-work.js +15 -3
  366. package/dist/slices/rebuild/recipe-youtube.js +4 -3
  367. package/dist/slices/rebuild/recovery-selection.js +3 -1
  368. package/dist/slices/rebuild/recovery.js +3 -2
  369. package/dist/slices/rebuild/repo.js +4 -3
  370. package/dist/slices/rebuild/review-promise.js +25 -0
  371. package/dist/slices/rebuild/review-redo.js +2 -1
  372. package/dist/slices/rebuild/runtime-admission.js +4 -3
  373. package/dist/slices/rebuild/runtime-agent-review.js +11 -5
  374. package/dist/slices/rebuild/runtime-animate.js +9 -8
  375. package/dist/slices/rebuild/runtime-article.js +3 -2
  376. package/dist/slices/rebuild/runtime-checkpoints.js +5 -4
  377. package/dist/slices/rebuild/runtime-document.js +8 -11
  378. package/dist/slices/rebuild/runtime-export-bed.js +6 -11
  379. package/dist/slices/rebuild/runtime-export-edit.js +6 -15
  380. package/dist/slices/rebuild/runtime-export-inputs.js +7 -7
  381. package/dist/slices/rebuild/runtime-export-short.js +8 -4
  382. package/dist/slices/rebuild/runtime-export.js +34 -12
  383. package/dist/slices/rebuild/runtime-figure-card.js +2 -1
  384. package/dist/slices/rebuild/runtime-image.js +4 -9
  385. package/dist/slices/rebuild/runtime-instructions.js +2 -0
  386. package/dist/slices/rebuild/runtime-keywords.js +59 -0
  387. package/dist/slices/rebuild/runtime-lines.js +2 -11
  388. package/dist/slices/rebuild/runtime-local.js +14 -20
  389. package/dist/slices/rebuild/runtime-narration-publication.js +2 -5
  390. package/dist/slices/rebuild/runtime-narration-reuse.js +9 -13
  391. package/dist/slices/rebuild/runtime-narration-text.js +10 -15
  392. package/dist/slices/rebuild/runtime-pictures.js +2 -4
  393. package/dist/slices/rebuild/runtime-piece-label.js +10 -6
  394. package/dist/slices/rebuild/runtime-plan.js +31 -5
  395. package/dist/slices/rebuild/runtime-provider.js +60 -19
  396. package/dist/slices/rebuild/runtime-publication.js +10 -9
  397. package/dist/slices/rebuild/runtime-review.js +33 -28
  398. package/dist/slices/rebuild/runtime-run.js +4 -1
  399. package/dist/slices/rebuild/runtime-shorts.js +25 -45
  400. package/dist/slices/rebuild/runtime-store.js +3 -8
  401. package/dist/slices/rebuild/runtime-subtitles.js +16 -14
  402. package/dist/slices/rebuild/runtime-voices.js +8 -15
  403. package/dist/slices/rebuild/runtime-youtube.js +11 -29
  404. package/dist/slices/rebuild/transition-repo.js +6 -5
  405. package/dist/slices/rebuild/work-records.js +6 -0
  406. package/dist/slices/reruns/cascade.js +1 -2
  407. package/dist/slices/reruns/index.js +6 -9
  408. package/dist/slices/reviews/external-model.js +3 -0
  409. package/dist/slices/reviews/external-repo.js +2 -1
  410. package/dist/slices/reviews/external-rows.js +2 -3
  411. package/dist/slices/reviews/model.js +26 -8
  412. package/dist/slices/reviews/repo.js +2 -1
  413. package/dist/slices/reviews/rules.js +1 -0
  414. package/dist/slices/reviews/view.js +5 -2
  415. package/dist/slices/revisions/downloads.js +4 -5
  416. package/dist/slices/revisions/mutation-assets.js +7 -4
  417. package/dist/slices/revisions/mutation-cues.js +2 -5
  418. package/dist/slices/revisions/mutations.js +0 -1
  419. package/dist/slices/revisions/repo.js +3 -1
  420. package/dist/slices/revisions/schema.js +1 -0
  421. package/dist/slices/revisions/view.js +20 -0
  422. package/dist/slices/run-cost/limits.js +5 -2
  423. package/dist/slices/run-cost/meter.js +12 -5
  424. package/dist/slices/run-cost/panel.js +8 -74
  425. package/dist/slices/run-cost/plan-readings.js +127 -0
  426. package/dist/slices/run-cost/plan-use.js +169 -0
  427. package/dist/slices/run-cost/pricing.js +3 -3
  428. package/dist/slices/run-cost/week.js +32 -4
  429. package/dist/slices/schedules/agenda.js +11 -2
  430. package/dist/slices/schedules/by-date.js +42 -0
  431. package/dist/slices/schedules/calendar.js +5 -0
  432. package/dist/slices/schedules/design-agenda.js +36 -0
  433. package/dist/slices/schedules/design-check.js +41 -0
  434. package/dist/slices/schedules/design-run.js +126 -0
  435. package/dist/slices/schedules/next-run.js +62 -0
  436. package/dist/slices/schedules/prepare.js +18 -2
  437. package/dist/slices/schedules/repo.js +24 -16
  438. package/dist/slices/schedules/rescore.js +35 -0
  439. package/dist/slices/schedules/scheduler.js +13 -3
  440. package/dist/slices/schedules/schema.js +54 -3
  441. package/dist/slices/schedules/score.js +107 -0
  442. package/dist/slices/schedules/service.js +57 -24
  443. package/dist/slices/schedules/topic-list.js +15 -0
  444. package/dist/slices/schedules/topic-scores.js +104 -0
  445. package/dist/slices/schedules/topics.js +48 -36
  446. package/dist/slices/settings/cli-paths.js +4 -6
  447. package/dist/slices/settings/first-run.js +2 -11
  448. package/dist/slices/settings/model-reach.js +3 -17
  449. package/dist/slices/settings/model.js +10 -0
  450. package/dist/slices/settings/playback.js +33 -22
  451. package/dist/slices/settings/probe-read.js +16 -0
  452. package/dist/slices/settings/repo.js +33 -0
  453. package/dist/slices/settings/tutorial.js +2 -2
  454. package/dist/slices/settings/voices.js +4 -12
  455. package/dist/slices/shorts/pick.js +2 -15
  456. package/dist/slices/shorts/prompts.js +2 -13
  457. package/dist/slices/shorts/render.js +3 -0
  458. package/dist/slices/social/channel.js +44 -0
  459. package/dist/slices/social/defaults.js +17 -0
  460. package/dist/slices/social/due.js +36 -0
  461. package/dist/slices/social/extension.js +18 -0
  462. package/dist/slices/social/fit.js +115 -0
  463. package/dist/slices/social/limits.js +186 -0
  464. package/dist/slices/social/links.js +21 -0
  465. package/dist/slices/social/media.js +131 -0
  466. package/dist/slices/social/model.js +75 -0
  467. package/dist/slices/social/notice.js +19 -0
  468. package/dist/slices/social/pack.js +176 -0
  469. package/dist/slices/social/post-prompt.js +55 -0
  470. package/dist/slices/social/queue.js +31 -0
  471. package/dist/slices/social/repo.js +133 -0
  472. package/dist/slices/social/reshape.js +97 -0
  473. package/dist/slices/social/rules.js +5 -0
  474. package/dist/slices/social/schema.js +70 -0
  475. package/dist/slices/social/serve.js +51 -0
  476. package/dist/slices/social/service.js +128 -0
  477. package/dist/slices/social/settings.js +21 -0
  478. package/dist/slices/social/sources.js +114 -0
  479. package/dist/slices/social/state.js +110 -0
  480. package/dist/slices/social/text.js +149 -0
  481. package/dist/slices/storage/arrange.js +3 -1
  482. package/dist/slices/storage/assets.js +8 -10
  483. package/dist/slices/storage/backup-designs.js +74 -0
  484. package/dist/slices/storage/backup-export.js +8 -3
  485. package/dist/slices/storage/backup-format.js +10 -0
  486. package/dist/slices/storage/backup-import.js +20 -2
  487. package/dist/slices/storage/delete-project.js +6 -5
  488. package/dist/slices/storage/files-location.js +6 -8
  489. package/dist/slices/storage/layout.js +13 -2
  490. package/dist/slices/storage/model.js +3 -0
  491. package/dist/slices/storage/portable.js +16 -11
  492. package/dist/slices/storage/schema.js +2 -0
  493. package/dist/slices/storage/staging-refs.js +2 -1
  494. package/dist/slices/storage/staging.js +6 -9
  495. package/dist/slices/storage/trim.js +10 -3
  496. package/dist/slices/studio/calendar.js +4 -1
  497. package/dist/slices/studio/pick.js +2 -11
  498. package/dist/slices/studio/plan.js +6 -15
  499. package/dist/slices/studio/queue.js +3 -14
  500. package/dist/slices/studio/releases.js +26 -0
  501. package/dist/slices/studio/settings.js +14 -51
  502. package/dist/slices/studio/videos.js +12 -0
  503. package/dist/slices/style-preview/render.js +34 -13
  504. package/dist/slices/style-preview/schema.js +13 -0
  505. package/dist/slices/style-preview/service.js +2 -1
  506. package/dist/slices/style-preview/settings.js +33 -5
  507. package/dist/slices/telemetry/collector-client.js +2 -4
  508. package/dist/slices/telemetry/flush.js +2 -4
  509. package/dist/slices/telemetry/model.js +0 -7
  510. package/dist/slices/telemetry/record.js +10 -3
  511. package/dist/slices/template-studio/anatomy-view.js +118 -0
  512. package/dist/slices/template-studio/anatomy.js +73 -0
  513. package/dist/slices/template-studio/bakeoff-make.js +225 -0
  514. package/dist/slices/template-studio/bakeoff-repo.js +83 -0
  515. package/dist/slices/template-studio/bakeoff.js +333 -0
  516. package/dist/slices/template-studio/budget.js +103 -0
  517. package/dist/slices/template-studio/checks.js +273 -0
  518. package/dist/slices/template-studio/document-sample.js +56 -0
  519. package/dist/slices/template-studio/edit.js +15 -0
  520. package/dist/slices/template-studio/for-all.js +161 -0
  521. package/dist/slices/template-studio/free-video.js +31 -0
  522. package/dist/slices/template-studio/free.js +176 -0
  523. package/dist/slices/template-studio/gate.js +40 -0
  524. package/dist/slices/template-studio/hidden.js +20 -0
  525. package/dist/slices/template-studio/jobs.js +79 -0
  526. package/dist/slices/template-studio/loudness.js +111 -0
  527. package/dist/slices/template-studio/make.js +40 -0
  528. package/dist/slices/template-studio/model.js +54 -0
  529. package/dist/slices/template-studio/pack-import.js +253 -0
  530. package/dist/slices/template-studio/pack-samples.js +60 -0
  531. package/dist/slices/template-studio/pack.js +205 -0
  532. package/dist/slices/template-studio/paid-view.js +66 -0
  533. package/dist/slices/template-studio/paid.js +348 -0
  534. package/dist/slices/template-studio/quote.js +148 -0
  535. package/dist/slices/template-studio/ready.js +104 -0
  536. package/dist/slices/template-studio/repo.js +134 -0
  537. package/dist/slices/template-studio/reviews-path.js +58 -0
  538. package/dist/slices/template-studio/runner.js +84 -0
  539. package/dist/slices/template-studio/save.js +236 -0
  540. package/dist/slices/template-studio/say-it-as.js +248 -0
  541. package/dist/slices/template-studio/schedule-gate.js +34 -0
  542. package/dist/slices/template-studio/schedule-price.js +30 -0
  543. package/dist/slices/template-studio/schedule-sample.js +87 -0
  544. package/dist/slices/template-studio/schema.js +218 -0
  545. package/dist/slices/template-studio/scope.js +124 -0
  546. package/dist/slices/template-studio/sections.js +176 -0
  547. package/dist/slices/template-studio/service.js +173 -0
  548. package/dist/slices/template-studio/status.js +174 -0
  549. package/dist/slices/template-studio/topic.js +223 -0
  550. package/dist/slices/trash/designs.js +76 -0
  551. package/dist/slices/trash/model.js +13 -4
  552. package/dist/slices/trash/service.js +52 -2
  553. package/dist/slices/tutorials/library.js +3 -2
  554. package/dist/slices/video/ass-text.js +17 -0
  555. package/dist/slices/video/audio-export.js +2 -64
  556. package/dist/slices/video/encoding.js +160 -0
  557. package/dist/slices/video/ffmpeg.js +23 -21
  558. package/dist/slices/video/figure-card.js +1 -15
  559. package/dist/slices/video/slideshow.js +43 -18
  560. package/dist/slices/voices/palette.js +0 -3
  561. package/dist/slices/youtube/answer.js +2 -15
  562. package/dist/slices/youtube/edits-repo.js +2 -11
  563. package/dist/tutorials/Agents-MCP.md +2 -1
  564. package/dist/tutorials/Assistant.md +42 -0
  565. package/dist/tutorials/Channels.md +2 -0
  566. package/dist/tutorials/Home-and-Projects.md +19 -0
  567. package/dist/tutorials/Install.md +1 -0
  568. package/dist/tutorials/Keywords-Slopify-Fills.md +87 -0
  569. package/dist/tutorials/Models.md +33 -1
  570. package/dist/tutorials/Notifications.md +6 -0
  571. package/dist/tutorials/Play-Images.md +4 -0
  572. package/dist/tutorials/Posting-to-Social-Sites.md +121 -0
  573. package/dist/tutorials/Prompts.md +10 -6
  574. package/dist/tutorials/Remote-Access.md +63 -0
  575. package/dist/tutorials/Reviews-and-Checkpoints.md +13 -2
  576. package/dist/tutorials/Schedules.md +21 -1
  577. package/dist/tutorials/Settings-Reference.md +50 -8
  578. package/dist/tutorials/Template-Studio.md +125 -0
  579. package/dist/tutorials/Templates.md +3 -0
  580. package/dist/tutorials/_Sidebar.md +5 -0
  581. package/dist/updater/work-in-progress.js +42 -12
  582. package/dist/web/assets/index-CkAI_2E7.css +1 -0
  583. package/dist/web/assets/index-DcD0cdfo.js +334 -0
  584. package/dist/web/assets/{pdf-T7MCwjIv.js → pdf-TODRwF31.js} +1 -1
  585. package/dist/web/index.html +2 -2
  586. package/package.json +5 -1
  587. package/dist/slices/article/provided-entries.js +0 -66
  588. package/dist/slices/article/run.js +0 -145
  589. package/dist/slices/images/run.js +0 -234
  590. package/dist/slices/narration/legacy-plan.js +0 -58
  591. package/dist/slices/narration/run.js +0 -270
  592. package/dist/slices/rebuild/runtime-actions.js +0 -90
  593. package/dist/slices/research/run.js +0 -224
  594. package/dist/slices/subtitles/prepare.js +0 -194
  595. package/dist/slices/subtitles/transcript.js +0 -34
  596. package/dist/slices/thumbnail/run.js +0 -219
  597. package/dist/slices/video/audio-inputs.js +0 -33
  598. package/dist/slices/video/reuse-audio.js +0 -62
  599. package/dist/slices/video/run.js +0 -78
  600. package/dist/slices/video/write-export.js +0 -158
  601. package/dist/slices/video/write-subtitles.js +0 -59
  602. package/dist/web/assets/index-BadsoORk.css +0 -1
  603. package/dist/web/assets/index-D8LqNsCl.js +0 -326
@@ -0,0 +1,282 @@
1
+ import { zValidator } from "@hono/zod-validator";
2
+ import { Hono } from "hono";
3
+ import { z } from "zod";
4
+ import { projectById } from "../../slices/admission/repo.js";
5
+ import { webhookBody } from "../../slices/notifications/address.js";
6
+ import { sendFailureText } from "../../slices/notifications/send.js";
7
+ import { readNotificationUrl } from "../../slices/notifications/settings.js";
8
+ import { waitsForRelease } from "../../slices/social/due.js";
9
+ import { postingExtension, writeExtensionSites } from "../../slices/social/extension.js";
10
+ import { platformLabels } from "../../slices/social/model.js";
11
+ import { postReadyNotice } from "../../slices/social/notice.js";
12
+ import { packFor, postPacks } from "../../slices/social/pack.js";
13
+ import { readPost, writeChoice, writeReddit, writeTextEdit } from "../../slices/social/repo.js";
14
+ import { choiceBody, linkBody, platformSchema, redditBody, resultBody, textBody, } from "../../slices/social/schema.js";
15
+ import { serveMedia } from "../../slices/social/serve.js";
16
+ import { duePosts, fillPost, linksToFind, nextPost, notPostingSentence, reportPost, setPostLink, } from "../../slices/social/service.js";
17
+ import { fileResponse } from "./byte-range.js";
18
+ import { extensionAuth } from "./extension-auth.js";
19
+ import { idString } from "./ids.js";
20
+ import { onInvalid, problem } from "./problem.js";
21
+ import { fromSlopifyPage } from "./same-page.js";
22
+ const projectParam = z.object({ projectId: idString });
23
+ const postParam = projectParam.extend({ platform: platformSchema });
24
+ const fileParam = projectParam.extend({
25
+ file: z
26
+ .string()
27
+ .min(1)
28
+ .max(200)
29
+ .regex(/^[A-Za-z0-9_-]+$/),
30
+ });
31
+ const nextQuery = z.object({
32
+ platform: platformSchema,
33
+ projectId: z.string().min(1).max(200).optional(),
34
+ });
35
+ const dueQuery = z.object({ hours: z.coerce.number().int().min(1).max(48).optional() });
36
+ const sitesBody = z.object({ sites: z.array(platformSchema).max(6) });
37
+ export function socialRoutes(deps) {
38
+ const { allowOrigin, allowed, refused } = extensionAuth(deps.db);
39
+ const media = {
40
+ db: deps.db,
41
+ paths: deps.paths,
42
+ reshaper: deps.social?.reshaper,
43
+ ...(deps.designs === undefined
44
+ ? {}
45
+ : {
46
+ renderDesign: async (designId, format, signal) => {
47
+ const drawn = await deps.designs?.renderer.render(designId, format, "png", signal);
48
+ return drawn?.ok === true
49
+ ? { ok: true, path: drawn.value.path }
50
+ : {
51
+ ok: false,
52
+ sentence: drawn?.message ??
53
+ "The design couldn't be drawn. Open it in Library → Designs to check it, then reload Prepare upload.",
54
+ };
55
+ },
56
+ }),
57
+ };
58
+ const samePage = (c) => fromSlopifyPage(c, deps.remoteAccess?.remoteHost)
59
+ ? undefined
60
+ : problem(c, {
61
+ status: 403,
62
+ detail: "For your safety, a project's posts can only be changed from the Slopify page itself. Open Slopify and try again there.",
63
+ });
64
+ const answer = (c, projectId) => {
65
+ const result = postPacks(deps, projectId, deps.clock.now());
66
+ if (!result.ok)
67
+ return unknownProject(c);
68
+ const body = {
69
+ ...result.packs,
70
+ extension: postingExtension(deps.db),
71
+ };
72
+ return c.json(body);
73
+ };
74
+ const refusal = (c, refused) => problem(c, { status: refused.status, detail: refused.sentence });
75
+ const posting = (c, projectId, platform) => packFor(deps, projectId, platform, deps.clock.now()) === undefined
76
+ ? problem(c, {
77
+ status: 404,
78
+ detail: notPostingSentence(platform),
79
+ })
80
+ : undefined;
81
+ // A post filled at its release time tells the Notification URL, when one is set. Never
82
+ // awaited: the extension's report doesn't wait on it.
83
+ const notify = (text, projectId) => {
84
+ const url = readNotificationUrl(deps.db);
85
+ const send = deps.sendNotification;
86
+ if (url === null || send === undefined)
87
+ return;
88
+ void send(url, webhookBody(text)).then((sent) => {
89
+ if (!sent.ok)
90
+ deps.log.write("warn", "social.notify", { projectId, detail: sendFailureText(sent) });
91
+ });
92
+ };
93
+ const changed = (projectId) => deps.hub.emit(projectId, { type: "project.updated", projectId });
94
+ return (new Hono()
95
+ .get("/packs/:projectId", zValidator("param", projectParam, onInvalid), (c) => answer(c, c.req.valid("param").projectId))
96
+ // The project's own pick for one site: other settings, not posting there ({off: true}),
97
+ // or back to the template's (null).
98
+ .put("/packs/:projectId/:platform/choice", zValidator("param", postParam, onInvalid), zValidator("json", choiceBody, onInvalid), (c) => {
99
+ const denied = samePage(c);
100
+ if (denied !== undefined)
101
+ return denied;
102
+ const { projectId, platform } = c.req.valid("param");
103
+ if (postPacks(deps, projectId, deps.clock.now()).ok === false)
104
+ return unknownProject(c);
105
+ const { choice } = c.req.valid("json");
106
+ const config = projectById(deps.db, projectId)?.config.social?.[platform];
107
+ if (choice !== null &&
108
+ !("off" in choice) &&
109
+ choice.text === "prompt" &&
110
+ config?.text !== "prompt")
111
+ return problem(c, {
112
+ status: 409,
113
+ detail: `A post written by a post prompt is a paid step, so it is picked in the template, in Template Studio → Upload pack, where Save shows its cost. For this project, pick a free text source in Prepare upload → ${platformLabels[platform]} → Settings.`,
114
+ });
115
+ writeChoice(deps.db, { projectId, platform }, choice, deps.clock.now());
116
+ changed(projectId);
117
+ return answer(c, projectId);
118
+ })
119
+ .put("/packs/:projectId/:platform/text", zValidator("param", postParam, onInvalid), zValidator("json", textBody, onInvalid), (c) => {
120
+ const denied = samePage(c);
121
+ if (denied !== undefined)
122
+ return denied;
123
+ const { projectId, platform } = c.req.valid("param");
124
+ const missing = posting(c, projectId, platform);
125
+ if (missing !== undefined)
126
+ return missing;
127
+ writeTextEdit(deps.db, { projectId, platform }, c.req.valid("json"), deps.clock.now());
128
+ changed(projectId);
129
+ return answer(c, projectId);
130
+ })
131
+ .delete("/packs/:projectId/:platform/text", zValidator("param", postParam, onInvalid), (c) => {
132
+ const denied = samePage(c);
133
+ if (denied !== undefined)
134
+ return denied;
135
+ const { projectId, platform } = c.req.valid("param");
136
+ const missing = posting(c, projectId, platform);
137
+ if (missing !== undefined)
138
+ return missing;
139
+ writeTextEdit(deps.db, { projectId, platform }, null, deps.clock.now());
140
+ changed(projectId);
141
+ return answer(c, projectId);
142
+ })
143
+ .put("/packs/:projectId/:platform/reddit", zValidator("param", postParam, onInvalid), zValidator("json", redditBody, onInvalid), (c) => {
144
+ const denied = samePage(c);
145
+ if (denied !== undefined)
146
+ return denied;
147
+ const { projectId, platform } = c.req.valid("param");
148
+ if (platform !== "reddit")
149
+ return problem(c, {
150
+ status: 400,
151
+ detail: "Only a Reddit post has a subreddit, title and flair. Open the Reddit post instead.",
152
+ });
153
+ const missing = posting(c, projectId, platform);
154
+ if (missing !== undefined)
155
+ return missing;
156
+ writeReddit(deps.db, { projectId, platform }, c.req.valid("json").reddit, deps.clock.now());
157
+ changed(projectId);
158
+ return answer(c, projectId);
159
+ })
160
+ // The post's link, pasted by the person (posted by hand, or the extension missed it); null
161
+ // removes it, so the post can be filled again.
162
+ .put("/packs/:projectId/:platform/link", zValidator("param", postParam, onInvalid), zValidator("json", linkBody, onInvalid), (c) => {
163
+ const denied = samePage(c);
164
+ if (denied !== undefined)
165
+ return denied;
166
+ const { projectId, platform } = c.req.valid("param");
167
+ const result = setPostLink(deps, projectId, platform, c.req.valid("json").url, deps.clock.now());
168
+ if (!result.ok)
169
+ return refusal(c, result);
170
+ changed(projectId);
171
+ return answer(c, projectId);
172
+ })
173
+ // Fill in <site>: never offered to agents or the assistant (`excluded-routes.ts`).
174
+ .post("/packs/:projectId/:platform/fill", zValidator("param", postParam, onInvalid), (c) => {
175
+ const denied = samePage(c);
176
+ if (denied !== undefined)
177
+ return denied;
178
+ const { projectId, platform } = c.req.valid("param");
179
+ const result = fillPost(deps, projectId, platform, deps.clock.now());
180
+ if (!result.ok)
181
+ return refusal(c, result);
182
+ changed(projectId);
183
+ return c.json({
184
+ pack: result.pack,
185
+ waitsForRelease: result.waitsForRelease,
186
+ url: result.pack.composeUrl,
187
+ });
188
+ })
189
+ .get("/media/:projectId/:file", zValidator("param", fileParam, onInvalid), async (c) => {
190
+ const { projectId, file } = c.req.valid("param");
191
+ return served(c, await serveMedia(media, projectId, file, deps.clock.now(), c.req.raw.signal));
192
+ })
193
+ // The extension's routes.
194
+ .options("/ext/*", (c) => {
195
+ allowOrigin(c, false);
196
+ return c.body(null, 204);
197
+ })
198
+ .get("/ext/next", zValidator("query", nextQuery, onInvalid), (c) => {
199
+ allowOrigin(c, false);
200
+ if (!allowed(c))
201
+ return refused(c);
202
+ const { platform, projectId } = c.req.valid("query");
203
+ const next = nextPost(deps, platform, deps.clock.now(), projectId);
204
+ if (next === undefined)
205
+ return problem(c, {
206
+ status: 404,
207
+ detail: "Nothing is waiting to be filled on this site. In Slopify, open the project, press Prepare upload, then Fill in the site's post.",
208
+ });
209
+ changed(next.pack.projectId);
210
+ return c.json(next);
211
+ })
212
+ .get("/ext/packs/:projectId/:platform", zValidator("param", postParam, onInvalid), (c) => {
213
+ allowOrigin(c, false);
214
+ if (!allowed(c))
215
+ return refused(c);
216
+ const { projectId, platform } = c.req.valid("param");
217
+ const pack = packFor(deps, projectId, platform, deps.clock.now());
218
+ return pack === undefined ? unknownPost(c) : c.json(pack);
219
+ })
220
+ .get("/ext/files/:projectId/:file", zValidator("param", fileParam, onInvalid), async (c) => {
221
+ allowOrigin(c, false);
222
+ if (!allowed(c))
223
+ return refused(c);
224
+ const { projectId, file } = c.req.valid("param");
225
+ return served(c, await serveMedia(media, projectId, file, deps.clock.now(), c.req.raw.signal));
226
+ })
227
+ .get("/ext/due", zValidator("query", dueQuery, onInvalid), (c) => {
228
+ allowOrigin(c, false);
229
+ if (!allowed(c))
230
+ return refused(c);
231
+ const hours = c.req.valid("query").hours ?? 24;
232
+ return c.json({ posts: duePosts(deps, deps.clock.now(), hours * 3_600_000) });
233
+ })
234
+ .post("/ext/result", zValidator("json", resultBody, onInvalid), (c) => {
235
+ allowOrigin(c, false);
236
+ if (!allowed(c))
237
+ return refused(c);
238
+ const body = c.req.valid("json");
239
+ const result = reportPost(deps, body, deps.clock.now());
240
+ if (!result.ok)
241
+ return refusal(c, result);
242
+ changed(body.projectId);
243
+ if (body.state === "filled" && waitsForRelease(body.platform) && result.pack !== undefined)
244
+ notify(postReadyNotice(result.pack), body.projectId);
245
+ return c.json({ status: readPost(deps.db, body).state });
246
+ })
247
+ .get("/ext/find-links", (c) => {
248
+ allowOrigin(c, false);
249
+ if (!allowed(c))
250
+ return refused(c);
251
+ return c.json({ posts: linksToFind(deps, deps.clock.now()) });
252
+ })
253
+ // Which sites the extension may post on, as its options last granted them.
254
+ .post("/ext/sites", zValidator("json", sitesBody, onInvalid), (c) => {
255
+ allowOrigin(c, false);
256
+ if (!allowed(c))
257
+ return refused(c);
258
+ writeExtensionSites(deps.db, c.req.valid("json").sites, deps.clock.now());
259
+ return c.json(postingExtension(deps.db));
260
+ }));
261
+ }
262
+ function served(c, result) {
263
+ if (!result.ok)
264
+ return problem(c, { status: result.status, detail: result.sentence });
265
+ return fileResponse(c, result.path, result.bytes, {
266
+ "content-type": result.contentType,
267
+ "content-disposition": `inline; filename="${result.filename.replace(/["\\]/g, "")}"`,
268
+ "x-content-type-options": "nosniff",
269
+ });
270
+ }
271
+ function unknownProject(c) {
272
+ return problem(c, {
273
+ status: 404,
274
+ detail: "This project no longer exists. Go back to Projects and open it again.",
275
+ });
276
+ }
277
+ function unknownPost(c) {
278
+ return problem(c, {
279
+ status: 404,
280
+ detail: "That project no longer posts to this site. In Slopify, open the project's Prepare upload and press Fill again.",
281
+ });
282
+ }
@@ -5,7 +5,7 @@ import { uploadableStageKinds } from "../../slices/storage/model.js";
5
5
  import { stagedFiles } from "../../slices/storage/repo.js";
6
6
  import { discardStagedFile, stageUpload } from "../../slices/storage/staging.js";
7
7
  import { readMultipart } from "./multipart.js";
8
- import { onInvalid, problem, titleOf } from "./problem.js";
8
+ import { onInvalid, problem } from "./problem.js";
9
9
  const kindParam = z.object({ kind: z.enum(uploadableStageKinds) });
10
10
  const idParam = z.object({ id: z.string().min(1).max(64) });
11
11
  // The return type is inferred on purpose: annotating it would erase the route types the
@@ -33,12 +33,11 @@ export function stagingRoutes(deps) {
33
33
  if (result === undefined) {
34
34
  return problem(c, {
35
35
  status: 400,
36
- title: titleOf(400),
37
36
  detail: "No file arrived with the upload. Choose the file again and retry.",
38
37
  });
39
38
  }
40
39
  if (!result.ok) {
41
- return problem(c, { status: 400, title: titleOf(400), detail: detailOf(result.reason) });
40
+ return problem(c, { status: 400, detail: detailOf(result.reason) });
42
41
  }
43
42
  return c.json(result.file, 201);
44
43
  })
@@ -47,7 +46,6 @@ export function stagingRoutes(deps) {
47
46
  if (!result.ok) {
48
47
  return problem(c, {
49
48
  status: result.reason === "in-use" ? 409 : 404,
50
- title: titleOf(result.reason === "in-use" ? 409 : 404),
51
49
  detail: result.reason === "in-use"
52
50
  ? "This file is still attached to a draft in Play. Remove it from the draft first."
53
51
  : "This uploaded file no longer exists; it may have been removed already. Reload the page.",
@@ -2,7 +2,9 @@ import { zValidator } from "@hono/zod-validator";
2
2
  import { Hono } from "hono";
3
3
  import { HTTPException } from "hono/http-exception";
4
4
  import { z } from "zod";
5
- import { onInvalid, problem, titleOf } from "./problem.js";
5
+ import { onInvalid, problem } from "./problem.js";
6
+ import { fromSlopifyPage } from "./same-page.js";
7
+ export const moveFilesBody = z.object({ target: z.string().min(1).max(4096) }).strict();
6
8
  // Settings → Backup & storage → Your files: where projects and backups are, Open folder, and
7
9
  // moving them. A move copies and checks every file, so it answers at once and the screen polls.
8
10
  export function filesRoutes(deps) {
@@ -16,23 +18,20 @@ export function filesRoutes(deps) {
16
18
  c.header("Cache-Control", "no-store");
17
19
  return c.json(await files().view());
18
20
  })
19
- .post("/move", zValidator("json", z.object({ target: z.string().min(1).max(4096) }).strict(), onInvalid), async (c) => {
21
+ .post("/move", zValidator("json", moveFilesBody, onInvalid), async (c) => {
20
22
  const started = await files().move(c.req.valid("json").target);
21
23
  if (!started.ok)
22
24
  return problem(c, {
23
25
  status: started.status,
24
- title: titleOf(started.status),
25
26
  detail: started.detail,
26
27
  extensions: { field: "target" },
27
28
  });
28
29
  return c.json(await files().view(), 202);
29
30
  })
30
31
  .post("/open", async (c) => {
31
- const origin = c.req.header("origin");
32
- if (origin !== undefined && origin !== new URL(c.req.url).origin)
32
+ if (!fromSlopifyPage(c, deps.remoteAccess?.remoteHost))
33
33
  return problem(c, {
34
34
  status: 403,
35
- title: titleOf(403),
36
35
  detail: "For your safety, folders can only be opened from the Slopify page itself. Open Slopify and try again there.",
37
36
  });
38
37
  try {
@@ -42,7 +41,6 @@ export function filesRoutes(deps) {
42
41
  deps.log.write("warn", "storage.open-files", { detail: String(error) });
43
42
  return problem(c, {
44
43
  status: 503,
45
- title: titleOf(503),
46
44
  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 open the folder shown in Settings → Backup & storage yourself.",
47
45
  });
48
46
  }
@@ -1,20 +1,14 @@
1
1
  import { zValidator } from "@hono/zod-validator";
2
2
  import { Hono } from "hono";
3
- import { z } from "zod";
4
3
  import { projectExists } from "../../slices/admission/repo.js";
5
4
  import { BackupBusyError, planBackup, streamBackup, } from "../../slices/storage/backup-export.js";
6
5
  import { BackupImportRefused, importBackup } from "../../slices/storage/backup-import.js";
7
6
  import { importPortable, portableMaxArchiveBytes, storageUsage, } from "../../slices/storage/portable.js";
8
7
  import { reconcileStorage } from "../../slices/storage/reconcile.js";
9
8
  import { cleanUpAll, deleteAllOldVersions, deleteOldVersions, keepOutputsOnly, projectStorage, } from "../../slices/storage/trim.js";
10
- import { onInvalid, problem, titleOf } from "./problem.js";
11
- const idParam = z.object({
12
- id: z
13
- .string()
14
- .min(1)
15
- .max(64)
16
- .regex(/^[0-9A-Za-z_-]+$/),
17
- });
9
+ import { sampleOwner } from "../../slices/template-studio/repo.js";
10
+ import { idParam } from "./ids.js";
11
+ import { onInvalid, problem } from "./problem.js";
18
12
  const exportFailed = "Slopify could not create the backup. Check there is free disk space and try again; if it keeps failing, use Download diagnostics in Settings and report it.";
19
13
  export function storageRoutes(deps) {
20
14
  const backup = () => ({
@@ -25,6 +19,17 @@ export function storageRoutes(deps) {
25
19
  appVersion: deps.version,
26
20
  hasInflight: deps.runner.hasInflight,
27
21
  });
22
+ // A template's studio sample keeps its files for the studio's bake-offs and resamples.
23
+ const studioSample = (id) => {
24
+ const owner = sampleOwner(deps.db, id);
25
+ return owner === undefined
26
+ ? undefined
27
+ : {
28
+ status: 409,
29
+ detail: `This is the studio sample of ${owner.name}, and the studio keeps its files to make its samples again without paying twice. Change it in its studio instead: Library → Templates → ${owner.name} → Open studio.`,
30
+ extensions: { reason: "template-sample" },
31
+ };
32
+ };
28
33
  return (new Hono()
29
34
  .get("/", (c) => c.json(storageUsage({ db: deps.db, paths: deps.paths, hasInflight: deps.runner.hasInflight })))
30
35
  // One project's split between outputs and working files, and what Keep outputs only
@@ -34,7 +39,6 @@ export function storageRoutes(deps) {
34
39
  if (!projectExists(deps.db, id))
35
40
  return problem(c, {
36
41
  status: 404,
37
- title: titleOf(404),
38
42
  detail: "This project no longer exists. Go back to Projects to pick another.",
39
43
  });
40
44
  c.header("Cache-Control", "no-store");
@@ -42,35 +46,37 @@ export function storageRoutes(deps) {
42
46
  })
43
47
  // Keep outputs only: a finished project drops its working files (`slices/storage/trim.ts`).
44
48
  .post("/projects/:id/keep-outputs", zValidator("param", idParam, onInvalid), (c) => {
49
+ const refused = studioSample(c.req.valid("param").id);
50
+ if (refused !== undefined)
51
+ return problem(c, refused);
45
52
  const result = keepOutputsOnly({ db: deps.db, paths: deps.paths, hasInflight: deps.runner.hasInflight }, c.req.valid("param").id);
46
53
  if (result.ok)
47
54
  return c.json(result);
48
55
  return result.reason === "no-project"
49
56
  ? problem(c, {
50
57
  status: 404,
51
- title: titleOf(404),
52
58
  detail: "This project no longer exists. Reload Settings → Storage to see the current list.",
53
59
  })
54
60
  : problem(c, {
55
61
  status: 409,
56
- title: titleOf(409),
57
62
  detail: "Only a finished project can drop its working files, and this one is running, waiting or has unfinished steps. Let it finish (or cancel it on its project page), then drop the working files again from the project page or Settings → Storage.",
58
63
  });
59
64
  })
60
65
  // Delete old versions: one project's History files (`slices/storage/trim.ts`).
61
66
  .post("/projects/:id/old-versions/delete", zValidator("param", idParam, onInvalid), (c) => {
67
+ const refused = studioSample(c.req.valid("param").id);
68
+ if (refused !== undefined)
69
+ return problem(c, refused);
62
70
  const result = deleteOldVersions({ db: deps.db, paths: deps.paths, hasInflight: deps.runner.hasInflight }, c.req.valid("param").id);
63
71
  if (result.ok)
64
72
  return c.json(result);
65
73
  return result.reason === "no-project"
66
74
  ? problem(c, {
67
75
  status: 404,
68
- title: titleOf(404),
69
76
  detail: "This project no longer exists. Reload Settings → Backup & storage to see the current list.",
70
77
  })
71
78
  : problem(c, {
72
79
  status: 409,
73
- title: titleOf(409),
74
80
  detail: "This project is running or has steps waiting to run, and its files stay where they are until then. Let it finish, or cancel its run on its project page, then press Delete old versions again.",
75
81
  });
76
82
  })
@@ -98,7 +104,7 @@ export function storageRoutes(deps) {
98
104
  if (error instanceof BackupBusyError)
99
105
  return c.json({ ready: false, detail: error.message, busy: error.projects });
100
106
  deps.log.write("error", "storage.export", { detail: String(error) });
101
- return problem(c, { status: 500, title: titleOf(500), detail: exportFailed });
107
+ return problem(c, { status: 500, detail: exportFailed });
102
108
  }
103
109
  })
104
110
  .get("/export", (c) => {
@@ -108,9 +114,9 @@ export function storageRoutes(deps) {
108
114
  }
109
115
  catch (error) {
110
116
  if (error instanceof BackupBusyError)
111
- return problem(c, { status: 409, title: titleOf(409), detail: error.message });
117
+ return problem(c, { status: 409, detail: error.message });
112
118
  deps.log.write("error", "storage.export", { detail: String(error) });
113
- return problem(c, { status: 500, title: titleOf(500), detail: exportFailed });
119
+ return problem(c, { status: 500, detail: exportFailed });
114
120
  }
115
121
  // Pulled one chunk at a time, so a slow download reads files no faster than it sends.
116
122
  const chunks = streamBackup(plan);
@@ -145,14 +151,12 @@ export function storageRoutes(deps) {
145
151
  if (!contentType.includes("application/zip"))
146
152
  return problem(c, {
147
153
  status: 415,
148
- title: titleOf(415),
149
154
  detail: "Choose a Slopify backup file: a .tar made with Export everything, or an older .zip made with Export backup, both in Settings → Backup & storage.",
150
155
  });
151
156
  const upload = await readPortableUpload(c.req.raw);
152
157
  if (!upload.ok)
153
158
  return problem(c, {
154
159
  status: 413,
155
- title: titleOf(413),
156
160
  detail: "This backup file is empty or larger than 100 MB. Choose a Slopify backup file under 100 MB.",
157
161
  });
158
162
  try {
@@ -167,7 +171,6 @@ export function storageRoutes(deps) {
167
171
  catch {
168
172
  return problem(c, {
169
173
  status: 400,
170
- title: titleOf(400),
171
174
  detail: "This file is not a complete Slopify backup; it may be damaged or a different kind of ZIP. Choose a backup made with Backup & storage in Settings.",
172
175
  });
173
176
  }
@@ -175,7 +178,6 @@ export function storageRoutes(deps) {
175
178
  if (request.body === null)
176
179
  return problem(c, {
177
180
  status: 400,
178
- title: titleOf(400),
179
181
  detail: "The backup file arrived empty. Choose the .tar made with Export everything and import it again.",
180
182
  });
181
183
  const reader = request.body.getReader();
@@ -186,13 +188,11 @@ export function storageRoutes(deps) {
186
188
  if (error instanceof BackupImportRefused)
187
189
  return problem(c, {
188
190
  status: error.status,
189
- title: titleOf(error.status),
190
191
  detail: error.message,
191
192
  });
192
193
  deps.log.write("error", "storage.import", { detail: String(error) });
193
194
  return problem(c, {
194
195
  status: 500,
195
- title: titleOf(500),
196
196
  detail: "The backup wasn't imported and nothing in this install changed. Try again; if it keeps failing, use Download diagnostics in Settings and report it.",
197
197
  });
198
198
  }