@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
@@ -0,0 +1,253 @@
1
+ import { rmSync } from "node:fs";
2
+ import { strFromU8, unzipSync } from "fflate";
3
+ import { transact } from "../../kernel/db/tx.js";
4
+ import { detectSlots } from "../admission/substitute.js";
5
+ import { nameMax } from "../library/model.js";
6
+ import { entryByName, insertEntry } from "../library/repo.js";
7
+ import { renamedIn } from "../library/used-by.js";
8
+ import { freeTemplateName, placePrompt } from "../onboarding/install.js";
9
+ import { createTemplate } from "../project-templates/service.js";
10
+ import { listVoices } from "../settings/repo.js";
11
+ import { looksSecret, packFormat, packFormatVersion, packLimits, packManifestSchema, packSampleFile, } from "./pack.js";
12
+ import { packSamplesDir, writePackSamples } from "./pack-samples.js";
13
+ const refuse = (message) => ({
14
+ ok: false,
15
+ reason: "invalid-input",
16
+ message,
17
+ });
18
+ const notAPack = "This file isn't a template file Slopify can read. Choose the .zip a template's Export as a file (.zip) made.";
19
+ class ZipStopped extends Error {
20
+ }
21
+ // Every check that needs nothing written: a zip, small enough, with a manifest of this format
22
+ // and version, no keys, and every sample it names inside it.
23
+ export function readPack(bytes) {
24
+ if (bytes.byteLength > packLimits.uploadBytes)
25
+ return refuse(`This template file is larger than ${String(packLimits.uploadBytes / 1024 / 1024)} MB, more than an exported template ever is. Export it again from the template's Edit template page, or ask whoever made it to.`);
26
+ let oversized;
27
+ let unexpected;
28
+ let twice;
29
+ let tooMany = false;
30
+ // A zip's central directory can list one compressed block many times over: entries are
31
+ // counted, named once each and capped in total before any of them is unpacked.
32
+ const maxEntries = 1 + packLimits.samples;
33
+ const maxTotal = packLimits.manifestBytes + packLimits.samples * packLimits.sampleBytes;
34
+ const seen = new Set();
35
+ let total = 0;
36
+ let entries;
37
+ try {
38
+ entries = unzipSync(bytes, {
39
+ filter: (file) => {
40
+ if (file.name.endsWith("/"))
41
+ return false;
42
+ if (seen.has(file.name)) {
43
+ twice ??= file.name;
44
+ throw new ZipStopped();
45
+ }
46
+ seen.add(file.name);
47
+ if (seen.size > maxEntries) {
48
+ tooMany = true;
49
+ throw new ZipStopped();
50
+ }
51
+ const known = file.name === "manifest.json" || packSampleFile.test(file.name);
52
+ if (!known) {
53
+ unexpected ??= file.name;
54
+ return false;
55
+ }
56
+ const limit = file.name === "manifest.json" ? packLimits.manifestBytes : packLimits.sampleBytes;
57
+ if (file.originalSize > limit) {
58
+ oversized ??= file.name;
59
+ return false;
60
+ }
61
+ total += file.originalSize;
62
+ if (total > maxTotal) {
63
+ tooMany = true;
64
+ throw new ZipStopped();
65
+ }
66
+ return true;
67
+ },
68
+ });
69
+ }
70
+ catch (error) {
71
+ if (!(error instanceof ZipStopped))
72
+ return refuse(notAPack);
73
+ entries = {};
74
+ }
75
+ if (tooMany)
76
+ return refuse(`This template file holds more files than an exported template ever has (a manifest and at most ${String(packLimits.samples)} samples), so nothing was imported. Export it again from the template's Edit template page.`);
77
+ if (twice !== undefined)
78
+ return refuse(`This template file holds ${twice.slice(0, 120)} twice, which an exported template never does, so nothing was imported. Export it again from the template's Edit template page.`);
79
+ if (oversized !== undefined)
80
+ return refuse(`This template file's ${oversized} is larger than a template file allows, so nothing was imported. Export it again from the template's Edit template page.`);
81
+ if (unexpected !== undefined)
82
+ return refuse(`This template file holds a file an exported template never has (${unexpected.slice(0, 120)}), so nothing was imported. Export it again from the template's Edit template page.`);
83
+ const raw = entries["manifest.json"];
84
+ if (raw === undefined)
85
+ return refuse("This zip has no manifest.json, so it isn't a template file. Choose the .zip a template's Export as a file (.zip) made.");
86
+ const text = strFromU8(raw);
87
+ if (looksSecret(text))
88
+ return refuse("This template file holds what looks like an API key or a login token, so Slopify didn't import it: keys never travel in a template file. Ask whoever made it to remove it from the template and export it again.");
89
+ let json;
90
+ try {
91
+ json = JSON.parse(text);
92
+ }
93
+ catch {
94
+ return refuse("This template file's manifest.json isn't readable JSON; the file may be damaged. Export it again from the template's Edit template page.");
95
+ }
96
+ const head = json;
97
+ if (typeof json !== "object" || json === null || head.format !== packFormat)
98
+ return refuse(notAPack);
99
+ if (typeof head.formatVersion === "number" && head.formatVersion > packFormatVersion)
100
+ return refuse(`This template file was made by a newer Slopify (file format ${String(head.formatVersion)}). Update Slopify in Settings → About, then import it again.`);
101
+ const parsed = packManifestSchema.safeParse(json);
102
+ if (!parsed.success) {
103
+ const issue = parsed.error.issues[0];
104
+ const where = issue === undefined ? "" : ` (${issue.path.join(".") || "manifest"}: ${issue.message})`;
105
+ return refuse(`This template file's manifest isn't one Slopify can use${where.slice(0, 300)}. It may be damaged or edited by hand; export it again from the template's Edit template page.`);
106
+ }
107
+ const files = new Map();
108
+ for (const sample of parsed.data.samples) {
109
+ const file = entries[sample.file];
110
+ if (file === undefined)
111
+ return refuse(`This template file names a sample (${sample.file}) it doesn't hold; the file may be damaged. Export it again from the template's Edit template page.`);
112
+ files.set(sample.file, file);
113
+ }
114
+ return { ok: true, value: { manifest: parsed.data, files } };
115
+ }
116
+ function placeEntry(deps, row, at) {
117
+ for (let n = 1;; n += 1) {
118
+ const suffix = n === 1 ? "" : ` (${String(n)})`;
119
+ const candidate = `${row.name.slice(0, nameMax - suffix.length).trimEnd()}${suffix}`;
120
+ const taken = entryByName(deps.db, row.category, candidate);
121
+ if (taken?.body === row.body)
122
+ return { name: taken.name, added: false };
123
+ if (taken !== undefined)
124
+ continue;
125
+ const entry = {
126
+ id: deps.ids.next(),
127
+ category: row.category,
128
+ mode: row.mode,
129
+ name: candidate,
130
+ body: row.body,
131
+ slots: detectSlots(row.body).names,
132
+ updatedAt: at,
133
+ };
134
+ insertEntry(deps.db, entry);
135
+ return { name: candidate, added: true };
136
+ }
137
+ }
138
+ // The pack's Library items placed in this Library, and the document naming each by the name it
139
+ // has here.
140
+ function placeLibrary(deps, document, at) {
141
+ const snapshot = document.librarySnapshot ?? { prompts: [], entries: [] };
142
+ let form = document.form;
143
+ let added = 0;
144
+ const renamed = [];
145
+ const prompts = [];
146
+ for (const row of snapshot.prompts) {
147
+ const placed = placePrompt(deps, row.kind, row.name, row.body, at);
148
+ if (placed.added)
149
+ added += 1;
150
+ if (placed.prompt.name !== row.name) {
151
+ renamed.push({ from: row.name, to: placed.prompt.name });
152
+ form =
153
+ renamedIn(form, { item: "prompt", kind: row.kind, name: row.name }, placed.prompt.name) ??
154
+ form;
155
+ }
156
+ prompts.push({ ...row, id: placed.prompt.id, name: placed.prompt.name });
157
+ }
158
+ const entries = [];
159
+ for (const row of snapshot.entries) {
160
+ const placed = placeEntry(deps, row, at);
161
+ if (placed.added)
162
+ added += 1;
163
+ if (placed.name !== row.name) {
164
+ renamed.push({ from: row.name, to: placed.name });
165
+ form =
166
+ renamedIn(form, { item: "entry", category: row.category, name: row.name }, placed.name) ??
167
+ form;
168
+ }
169
+ entries.push({ ...row, name: placed.name });
170
+ }
171
+ return {
172
+ document: {
173
+ ...document,
174
+ form: form,
175
+ librarySnapshot: { prompts, entries },
176
+ },
177
+ added,
178
+ renamed,
179
+ };
180
+ }
181
+ export function importPack(deps, bytes) {
182
+ const read = readPack(bytes);
183
+ if (!read.ok)
184
+ return read;
185
+ const { manifest, files } = read.value;
186
+ const templateId = deps.uuid();
187
+ const at = deps.clock.now().toISOString();
188
+ let made;
189
+ try {
190
+ made = transact(deps.db, () => {
191
+ const placed = placeLibrary(deps, manifest.template.document, at);
192
+ const { channelId: _channel, templateSource: _source, ...document } = placed.document;
193
+ const name = freeTemplateName(deps, manifest.template.name);
194
+ const created = createTemplate(deps, {
195
+ id: templateId,
196
+ name,
197
+ document: { ...document, fontUpload: null },
198
+ });
199
+ if (!created.ok)
200
+ throw new PackRefused(created.reason === "missing-prompt"
201
+ ? "This file's template names a prompt or intro/outro the file doesn't carry, so it can't be made here. Ask whoever made it to export it again."
202
+ : "Slopify couldn't make a template from this file. Import it again; if it fails again, use Download diagnostics in Settings and report it.");
203
+ const audio = created.value.document.form.audio;
204
+ const voiceKnown = audio.voice === "" ||
205
+ listVoices(deps.db).some((voice) => voice.provider === audio.provider && voice.voiceId === audio.voice);
206
+ return {
207
+ ok: true,
208
+ value: {
209
+ templateId,
210
+ name,
211
+ added: placed.added,
212
+ renamed: placed.renamed,
213
+ voiceKnown,
214
+ samples: manifest.samples.length,
215
+ },
216
+ };
217
+ });
218
+ }
219
+ catch (error) {
220
+ if (error instanceof PackRefused)
221
+ return refuse(error.message);
222
+ throw error;
223
+ }
224
+ if (!made.ok)
225
+ return made;
226
+ // The samples are only to look at: written once the template is in, a failure to write them
227
+ // leaves the template as it is, without them.
228
+ try {
229
+ writePackSamples(deps.paths, templateId, manifest.samples.flatMap((sample) => {
230
+ const body = files.get(sample.file);
231
+ return body === undefined
232
+ ? []
233
+ : [
234
+ {
235
+ file: sample.file.slice("samples/".length),
236
+ role: sample.role,
237
+ name: sample.name,
238
+ body,
239
+ },
240
+ ];
241
+ }));
242
+ }
243
+ catch (error) {
244
+ rmSync(packSamplesDir(deps.paths, templateId), { recursive: true, force: true });
245
+ deps.log.write("warn", "template-studio.pack", {
246
+ detail: `The samples of the imported pack ${templateId} weren't kept: ${error instanceof Error ? error.message : String(error)}`,
247
+ });
248
+ return { ok: true, value: { ...made.value, samples: 0 } };
249
+ }
250
+ return made;
251
+ }
252
+ class PackRefused extends Error {
253
+ }
@@ -0,0 +1,60 @@
1
+ import { existsSync, mkdirSync, readFileSync, renameSync, rmSync, statSync, writeFileSync, } from "node:fs";
2
+ import { join } from "node:path";
3
+ import { z } from "zod";
4
+ import { packSampleRoles, packSampleType } from "./pack.js";
5
+ // The samples an imported pack came with, kept beside the data dir's studio files so the studio
6
+ // shows them until the template makes its own: `template-studio/packs/<template id>/`, with a
7
+ // samples.json naming each file and what it is.
8
+ export function packSamplesDir(paths, templateId) {
9
+ return join(paths.dataDir, "template-studio", "packs", templateId);
10
+ }
11
+ const fileName = /^[a-z0-9-]{1,40}\.(png|jpg|jpeg|webp|mp3|m4a|wav)$/;
12
+ const listSchema = z.array(z
13
+ .object({
14
+ file: z.string().regex(fileName),
15
+ role: z.enum(packSampleRoles),
16
+ name: z.string().max(200),
17
+ })
18
+ .strict());
19
+ // Written to a folder of its own first and moved into place, so a half-written set is never
20
+ // shown.
21
+ export function writePackSamples(paths, templateId, samples) {
22
+ if (samples.length === 0)
23
+ return;
24
+ const target = packSamplesDir(paths, templateId);
25
+ const staging = `${target}.part`;
26
+ rmSync(staging, { recursive: true, force: true });
27
+ mkdirSync(staging, { recursive: true, mode: 0o700 });
28
+ for (const sample of samples) {
29
+ if (!fileName.test(sample.file))
30
+ continue;
31
+ writeFileSync(join(staging, sample.file), sample.body, { mode: 0o600 });
32
+ }
33
+ writeFileSync(join(staging, "samples.json"), JSON.stringify(samples.map(({ file, role, name }) => ({ file, role, name }))), { mode: 0o600 });
34
+ rmSync(target, { recursive: true, force: true });
35
+ renameSync(staging, target);
36
+ }
37
+ export function packSamples(paths, templateId) {
38
+ const list = join(packSamplesDir(paths, templateId), "samples.json");
39
+ if (!existsSync(list))
40
+ return [];
41
+ try {
42
+ const parsed = listSchema.safeParse(JSON.parse(readFileSync(list, "utf8")));
43
+ return parsed.success ? parsed.data : [];
44
+ }
45
+ catch {
46
+ return [];
47
+ }
48
+ }
49
+ // One kept sample's file, or undefined when the template has no such sample.
50
+ export function packSampleFileOf(paths, templateId, file) {
51
+ if (!packSamples(paths, templateId).some((sample) => sample.file === file))
52
+ return undefined;
53
+ const path = join(packSamplesDir(paths, templateId), file);
54
+ const type = packSampleType(`samples/${file}`);
55
+ const stat = statSync(path, { throwIfNoEntry: false });
56
+ return stat === undefined || type === undefined ? undefined : { path, bytes: stat.size, type };
57
+ }
58
+ export function removePackSamples(paths, templateId) {
59
+ rmSync(packSamplesDir(paths, templateId), { recursive: true, force: true });
60
+ }
@@ -0,0 +1,207 @@
1
+ import { readFile } from "node:fs/promises";
2
+ import { strToU8, zipSync } from "fflate";
3
+ import { z } from "zod";
4
+ import { redact } from "../../kernel/log.js";
5
+ import { projectExists } from "../admission/repo.js";
6
+ import { playDraftDocumentSchema } from "../play-drafts/schema.js";
7
+ import { templateById } from "../project-templates/repo.js";
8
+ import { templateNameMax } from "../project-templates/schema.js";
9
+ import { templateSetup } from "../project-templates/setup.js";
10
+ import { findRevisionDownload } from "../revisions/downloads.js";
11
+ import { currentRevisionId } from "../revisions/repo.js";
12
+ import { getRevisionView } from "../revisions/view.js";
13
+ import { listVoices } from "../settings/repo.js";
14
+ import { runnableVersion } from "./gate.js";
15
+ import { gatesOf, sampleOf } from "./repo.js";
16
+ // A template pack: a template set up in the studio, as one zip someone else imports and looks
17
+ // at before using it. It carries the version marked ready (its prompts' and intros' text, the
18
+ // voice it picks, the Look, the PDF theme) and a few of its sample's files. Never a key, a
19
+ // login or anything else of this computer's: a pack is made to be shared.
20
+ export const packFormat = "slopify-template-pack";
21
+ export const packFormatVersion = 1;
22
+ // ceiling: a pack is a few sample files and a manifest; a bigger upload is refused before it is
23
+ // read, and each entry is refused by its stated size before it is unpacked.
24
+ export const packLimits = {
25
+ uploadBytes: 30 * 1024 * 1024,
26
+ manifestBytes: 2 * 1024 * 1024,
27
+ sampleBytes: 6 * 1024 * 1024,
28
+ samples: 6,
29
+ pictures: 4,
30
+ };
31
+ export const packSampleRoles = ["thumbnail", "picture", "narration"];
32
+ const sampleTypes = {
33
+ png: "image/png",
34
+ jpg: "image/jpeg",
35
+ jpeg: "image/jpeg",
36
+ webp: "image/webp",
37
+ mp3: "audio/mpeg",
38
+ m4a: "audio/mp4",
39
+ wav: "audio/wav",
40
+ };
41
+ export const packSampleFile = /^samples\/([a-z0-9-]{1,40})\.(png|jpg|jpeg|webp|mp3|m4a|wav)$/;
42
+ export function packSampleType(file) {
43
+ const extension = packSampleFile.exec(file)?.[2];
44
+ return extension === undefined ? undefined : sampleTypes[extension];
45
+ }
46
+ export const packManifestSchema = z
47
+ .object({
48
+ format: z.literal(packFormat),
49
+ formatVersion: z.literal(packFormatVersion),
50
+ exportedAt: z.string().max(40),
51
+ slopifyVersion: z.string().max(40),
52
+ template: z
53
+ .object({
54
+ name: z.string().trim().min(1).max(templateNameMax),
55
+ version: z.number().int().positive(),
56
+ document: playDraftDocumentSchema,
57
+ })
58
+ .strict(),
59
+ // The voice the template picks, as the exporter's Settings → Voices names it.
60
+ voice: z
61
+ .object({
62
+ provider: z.string().min(1).max(100),
63
+ voiceId: z.string().min(1).max(200),
64
+ name: z.string().max(200),
65
+ })
66
+ .strict()
67
+ .nullable(),
68
+ samples: z
69
+ .array(z
70
+ .object({
71
+ file: z.string().regex(packSampleFile),
72
+ role: z.enum(packSampleRoles),
73
+ name: z.string().max(200),
74
+ })
75
+ .strict())
76
+ .max(packLimits.samples),
77
+ })
78
+ .strict();
79
+ // Whether text holds what looks like an API key or a bearer token (`kernel/log.ts`'s filter).
80
+ export function looksSecret(text) {
81
+ return redact(text) !== text;
82
+ }
83
+ const extensionOf = (path) => {
84
+ const extension = path.split(".").pop()?.toLowerCase();
85
+ return extension !== undefined && Object.hasOwn(sampleTypes, extension)
86
+ ? extension
87
+ : undefined;
88
+ };
89
+ // The sample's thumbnail, up to four pictures and its narration, as its project holds them now.
90
+ // A file too big for a pack is left out.
91
+ export function sampleFilesOf(deps, templateId) {
92
+ const sample = sampleOf(deps.db, templateId);
93
+ if (sample === undefined || !projectExists(deps.db, sample.projectId))
94
+ return [];
95
+ const revisionId = currentRevisionId(deps.db, sample.projectId);
96
+ if (revisionId === undefined)
97
+ return [];
98
+ const view = getRevisionView({ ...deps, ids: { next: () => "" }, clock: { now: deps.clock.now, sleep: async () => { } } }, sample.projectId, revisionId);
99
+ if (view === undefined)
100
+ return [];
101
+ const wanted = [
102
+ { role: "thumbnail", roles: ["thumbnail"] },
103
+ { role: "picture", roles: ["image"] },
104
+ { role: "narration", roles: ["audio_export", "audio_mp3", "audio_body"] },
105
+ ];
106
+ const files = [];
107
+ for (const { role, roles } of wanted) {
108
+ const limit = role === "picture" ? packLimits.pictures : 1;
109
+ let taken = 0;
110
+ for (const wantedRole of roles)
111
+ for (const row of view.outputs) {
112
+ if (taken >= limit)
113
+ break;
114
+ if (!row.selected || !row.available || row.state !== "ready")
115
+ continue;
116
+ if (row.output.role !== wantedRole)
117
+ continue;
118
+ const found = findRevisionDownload(deps, sample.projectId, revisionId, row.recordId);
119
+ if (!found.ok)
120
+ continue;
121
+ const extension = extensionOf(found.download.path);
122
+ if (extension === undefined || found.download.bytes > packLimits.sampleBytes)
123
+ continue;
124
+ taken += 1;
125
+ files.push({
126
+ role,
127
+ name: row.output.path.split("/").pop() ?? `${role}.${extension}`,
128
+ path: found.download.path,
129
+ bytes: found.download.bytes,
130
+ extension,
131
+ });
132
+ }
133
+ }
134
+ return files;
135
+ }
136
+ const slug = (name) => name
137
+ .toLowerCase()
138
+ .replace(/[^a-z0-9]+/g, "-")
139
+ .replace(/^-+|-+$/g, "")
140
+ .slice(0, 60) || "template";
141
+ // Export pack: the newest version its schedules may run (marked ready, carried, or saved
142
+ // before the studio), with the Library's text of each prompt it names as it is now.
143
+ export async function exportPack(deps, templateId, samples = (id) => sampleFilesOf(deps, id)) {
144
+ const head = templateById(deps.db, templateId);
145
+ if (head === undefined)
146
+ return {
147
+ ok: false,
148
+ reason: "not-found",
149
+ message: "This template no longer exists; it may have been deleted. Go to Templates to see your templates.",
150
+ };
151
+ // What its schedules run: the newest version, or once it was first approved for schedules,
152
+ // the newest approved one.
153
+ const runs = runnableVersion(gatesOf(deps.db, templateId), head.version);
154
+ const template = runs === undefined ? undefined : templateById(deps.db, templateId, runs);
155
+ if (template === undefined)
156
+ return {
157
+ ok: false,
158
+ reason: "not-ready",
159
+ message: "No version of this template is approved for schedules, so there is nothing to put in the file: it carries what its schedules run. Approve a version in Edit template, then export it again.",
160
+ };
161
+ const setup = templateSetup(deps, template.document);
162
+ if (!setup.ok)
163
+ return {
164
+ ok: false,
165
+ reason: "invalid-input",
166
+ message: `Version ${String(template.version)} names a prompt or intro/outro that was deleted, so its text can't go into the file. Choose another in Edit template, Save, then export it again.`,
167
+ };
168
+ const document = setup.value;
169
+ const { audio } = document.form;
170
+ const saved = listVoices(deps.db).find((voice) => voice.provider === audio.provider && voice.voiceId === audio.voice);
171
+ const files = samples(templateId);
172
+ const named = files.map((file, index) => ({
173
+ ...file,
174
+ entry: `samples/${file.role}-${String(index + 1)}.${file.extension}`,
175
+ }));
176
+ const manifest = {
177
+ format: packFormat,
178
+ formatVersion: packFormatVersion,
179
+ exportedAt: deps.clock.now().toISOString(),
180
+ slopifyVersion: deps.version.slice(0, 40),
181
+ template: { name: template.name, version: template.version, document },
182
+ voice: audio.provider === "" || audio.voice === ""
183
+ ? null
184
+ : { provider: audio.provider, voiceId: audio.voice, name: saved?.name ?? audio.voice },
185
+ samples: named.map((file) => ({ file: file.entry, role: file.role, name: file.name })),
186
+ };
187
+ const json = JSON.stringify(manifest, null, 2);
188
+ if (looksSecret(json))
189
+ return {
190
+ ok: false,
191
+ reason: "invalid-input",
192
+ message: "Something in this template looks like an API key or a login token (a keyword's value or a prompt's text), so no file was made: it is meant to be shared. Remove it in Edit template, Save, then export it again.",
193
+ };
194
+ const entries = { "manifest.json": [strToU8(json), { level: 6 }] };
195
+ for (const file of named) {
196
+ // Pictures and audio are compressed already: stored as they are.
197
+ entries[file.entry] = [new Uint8Array(await readFile(file.path)), { level: 0 }];
198
+ }
199
+ return {
200
+ ok: true,
201
+ value: {
202
+ filename: `${slug(template.name)}.slopify-template.zip`,
203
+ bytes: zipSync(entries),
204
+ version: template.version,
205
+ },
206
+ };
207
+ }
@@ -0,0 +1,66 @@
1
+ import { projectExists } from "../admission/repo.js";
2
+ import { templateById } from "../project-templates/repo.js";
3
+ import { currentRevisionId } from "../revisions/repo.js";
4
+ import { getRevisionView } from "../revisions/view.js";
5
+ import { runningJob } from "./jobs.js";
6
+ import { sampleOf, sampleVersions } from "./repo.js";
7
+ import { sectionOfWork } from "./sections.js";
8
+ import { paidStatuses } from "./status.js";
9
+ export function paidView(deps, templateId, head) {
10
+ const versions = sampleVersions(deps.db, templateId);
11
+ const row = versions.find((one) => one.templateVersion === head.version);
12
+ const sample = sampleOf(deps.db, templateId);
13
+ const projectId = sample !== undefined && projectExists(deps.db, sample.projectId) ? sample.projectId : null;
14
+ const revisionId = projectId === null ? null : (currentRevisionId(deps.db, projectId) ?? null);
15
+ const revision = projectId === null || revisionId === null
16
+ ? undefined
17
+ : getRevisionView({
18
+ db: deps.db,
19
+ paths: deps.paths,
20
+ log: deps.log,
21
+ ids: { next: () => "" },
22
+ clock: { now: () => new Date(), sleep: async () => undefined },
23
+ }, projectId, revisionId);
24
+ const outputs = revision === undefined || projectId === null || revisionId === null
25
+ ? []
26
+ : revision.outputs.flatMap((row) => {
27
+ const section = sectionOfWork(row.workKey);
28
+ if (!row.selected || !row.available || row.state !== "ready" || section === undefined)
29
+ return [];
30
+ return [
31
+ {
32
+ section,
33
+ role: row.output.role,
34
+ url: `/files/${encodeURIComponent(projectId)}/revisions/${encodeURIComponent(revisionId)}/${encodeURIComponent(row.recordId)}`,
35
+ name: row.output.path.split("/").pop() ?? row.output.path,
36
+ },
37
+ ];
38
+ });
39
+ const running = runningJob(deps.db);
40
+ return {
41
+ view: {
42
+ projectId,
43
+ revisionId,
44
+ state: row?.state ?? null,
45
+ sentence: row?.heldReason ?? null,
46
+ quotedHigh: row?.quotedHigh ?? null,
47
+ article: revision?.articleMarkdown ?? null,
48
+ outputs,
49
+ versions: versions.map((one) => ({
50
+ version: one.templateVersion,
51
+ state: one.state,
52
+ updatedAt: one.updatedAt,
53
+ })),
54
+ },
55
+ statuses: paidStatuses({
56
+ headVersion: head.version,
57
+ headDocument: head.document,
58
+ versions,
59
+ documentOf: (version) => templateById(deps.db, templateId, version)?.document,
60
+ running: running?.templateId === templateId &&
61
+ running.templateVersion === head.version &&
62
+ running.kind !== "free",
63
+ busyElsewhere: running !== undefined && running.templateId !== templateId,
64
+ }),
65
+ };
66
+ }