@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,194 @@
1
+ import { errorText } from "../../kernel/errors.js";
2
+ import { defaultChannelId } from "../channels/model.js";
3
+ import { projectChannels, templateChannelId } from "../channels/repo.js";
4
+ import { choiceSites, retiredModelUsage, slotLabels } from "../model-upkeep/usage.js";
5
+ import { weekSummary } from "../run-cost/week.js";
6
+ import { calendarRange } from "../schedules/agenda.js";
7
+ import { scheduleRows } from "../schedules/repo.js";
8
+ import { bufferForecast } from "./buffer.js";
9
+ import { forecastDays } from "./model.js";
10
+ import { readSnoozes, weeklyNotifyOf } from "./repo.js";
11
+ import { warningsOf } from "./warnings.js";
12
+ import { weeklyReport } from "./weekly.js";
13
+ const dayMs = 24 * 60 * 60_000;
14
+ // ceiling: one probe that hangs is skipped after this, so the hourly check always ends.
15
+ export const probeTimeoutMs = 15_000;
16
+ const clis = {
17
+ "claude-code": { name: "Claude Code", signIn: "claude auth login" },
18
+ codex: { name: "Codex", signIn: "codex login" },
19
+ gemini: { name: "Antigravity CLI", signIn: "agy" },
20
+ };
21
+ const cliOfProvider = (provider) => provider === "codex-image"
22
+ ? "codex"
23
+ : provider === "claude-code" || provider === "codex" || provider === "gemini"
24
+ ? provider
25
+ : undefined;
26
+ const live = (schedule) => schedule.deletedAt === null && (schedule.status === "active" || schedule.status === "paused");
27
+ // A design schedule makes designed pieces from dated rows: no video fills a slot, no topic runs
28
+ // out and no CLI writes it, so the forecast and its warnings leave it out entirely.
29
+ const makesVideos = (schedule) => schedule.templateKind !== "design";
30
+ // The CLIs an active schedule's template or topic finding chooses: the only ones worth asking.
31
+ export function clisInUse(db) {
32
+ const schedules = scheduleRows(db).filter((one) => one.deletedAt === null && one.status === "active" && makesVideos(one));
33
+ const templates = new Set(schedules.map((one) => one.templateId));
34
+ const ids = new Set(schedules.map((one) => one.id));
35
+ const found = new Set();
36
+ for (const site of choiceSites(db)) {
37
+ const used = (site.kind === "template" && templates.has(site.id)) ||
38
+ (site.kind === "schedule" && ids.has(site.id));
39
+ if (!used)
40
+ continue;
41
+ for (const choice of Object.values(site.choices)) {
42
+ const cli = choice === undefined ? undefined : cliOfProvider(choice.provider);
43
+ if (cli !== undefined)
44
+ found.add(cli);
45
+ }
46
+ }
47
+ for (const schedule of schedules) {
48
+ const cli = cliOfProvider(schedule.topicGeneration.llm?.provider ?? "");
49
+ if (schedule.topicGeneration.mode !== "off" && cli !== undefined)
50
+ found.add(cli);
51
+ }
52
+ return [...found].sort();
53
+ }
54
+ async function within(deps, what, probe) {
55
+ const signal = AbortSignal.timeout(probeTimeoutMs);
56
+ try {
57
+ return await Promise.race([
58
+ probe(signal),
59
+ new Promise((_resolve, reject) => {
60
+ signal.addEventListener("abort", () => reject(new Error(`${what} took too long`)), {
61
+ once: true,
62
+ });
63
+ }),
64
+ ]);
65
+ }
66
+ catch (error) {
67
+ deps.log.write("warn", "autopilot.probe", { detail: `${what}: ${errorText(error)}` });
68
+ return undefined;
69
+ }
70
+ }
71
+ export async function collectProbes(deps) {
72
+ const asked = await Promise.all(clisInUse(deps.db).map(async (id) => {
73
+ const login = deps.cliLogin;
74
+ if (login === undefined)
75
+ return undefined;
76
+ const answer = await within(deps, `${clis[id].name} sign-in`, (signal) => login(id, signal));
77
+ return answer === undefined ? undefined : [id, answer];
78
+ }));
79
+ const key = deps.openRouterKey;
80
+ const disk = deps.disk;
81
+ const [openRouter, space] = await Promise.all([
82
+ key === undefined ? undefined : within(deps, "OpenRouter key", key),
83
+ disk === undefined ? undefined : within(deps, "free disk space", () => disk()),
84
+ ]);
85
+ return {
86
+ checkedAt: deps.clock.now().toISOString(),
87
+ clis: new Map(asked.flatMap((one) => (one === undefined ? [] : [one]))),
88
+ openRouter: openRouter ?? null,
89
+ disk: space ?? null,
90
+ };
91
+ }
92
+ // The hourly check's last answers, held in memory: Home reads them and never waits for a CLI.
93
+ export function createAutopilotHealth(deps) {
94
+ let latest;
95
+ let running;
96
+ return {
97
+ latest: () => latest,
98
+ refresh: () => {
99
+ running ??= collectProbes(deps)
100
+ .then((probes) => {
101
+ latest = probes;
102
+ return probes;
103
+ })
104
+ .finally(() => {
105
+ running = undefined;
106
+ });
107
+ return running;
108
+ },
109
+ };
110
+ }
111
+ // Home's autopilot block for every channel, or one: the forecast and warnings from the
112
+ // database now and the probes' last answers, and the last seven days.
113
+ export function autopilotView(deps, probes, channelId) {
114
+ const now = deps.clock.now();
115
+ const until = new Date(now.valueOf() + forecastDays * dayMs);
116
+ const inChannel = (templateId) => channelId === undefined || templateChannelId(deps.db, templateId) === channelId;
117
+ const schedules = scheduleRows(deps.db).filter((one) => live(one) && makesVideos(one) && inChannel(one.templateId));
118
+ const ahead = calendarRange(deps, now, until);
119
+ // Finished videos not uploaded: the calendar's longest look back.
120
+ const behind = calendarRange(deps, new Date(now.valueOf() - 91 * dayMs), now);
121
+ const channels = projectChannels(deps.db);
122
+ const ready = behind.ok
123
+ ? behind.value.projects.filter((one) => one.readyToUpload === true &&
124
+ one.setAside !== true &&
125
+ (channelId === undefined || (channels.get(one.id) ?? defaultChannelId) === channelId)).length
126
+ : 0;
127
+ // A run its template has no version for is skipped: it fills no slot.
128
+ const runs = ahead.ok
129
+ ? ahead.value.runs.filter((run) => makesVideos(run) && inChannel(run.templateId) && run.notReady !== true)
130
+ : [];
131
+ const forecast = bufferForecast({
132
+ now,
133
+ until,
134
+ runs,
135
+ ready,
136
+ schedules: schedules.map((one) => ({
137
+ id: one.id,
138
+ name: one.name,
139
+ paused: one.status === "paused",
140
+ queued: one.items.length,
141
+ generation: one.topicGeneration.mode,
142
+ generationFailing: one.topics.error !== null,
143
+ cadence: one.cadence,
144
+ timezone: one.timezone,
145
+ })),
146
+ });
147
+ const active = new Set(schedules.filter((one) => one.status === "active").map((one) => one.id));
148
+ const templates = new Set(schedules.filter((one) => active.has(one.id)).map((one) => one.templateId));
149
+ const catalogue = deps.catalogue?.();
150
+ const signals = {
151
+ clis: [...(probes?.clis ?? new Map())].map(([id, login]) => ({
152
+ id,
153
+ ...clis[id],
154
+ login,
155
+ })),
156
+ plans: weekSummary(deps.db, now.toISOString()).plans,
157
+ openRouter: probes?.openRouter ?? null,
158
+ disk: probes?.disk ?? null,
159
+ schedules: schedules.map((one) => ({
160
+ id: one.id,
161
+ name: one.name,
162
+ paused: one.status === "paused",
163
+ queued: one.items.length,
164
+ generation: one.topicGeneration.mode,
165
+ topicsError: one.topics.error,
166
+ notReadyTemplate: deps.notReady?.(one.templateId) === undefined
167
+ ? null
168
+ : (deps.template(one.templateId)?.name ?? "this schedule's template"),
169
+ })),
170
+ retired: catalogue === undefined
171
+ ? []
172
+ : retiredModelUsage(deps.db, catalogue).flatMap((use) => (use.kind === "template" && templates.has(use.id)) ||
173
+ (use.kind === "schedule" && active.has(use.id))
174
+ ? [
175
+ {
176
+ kind: use.kind,
177
+ id: use.id,
178
+ name: use.name,
179
+ slot: slotLabels[use.slot],
180
+ model: use.model,
181
+ why: use.why,
182
+ replacement: use.replacement?.name ?? null,
183
+ },
184
+ ]
185
+ : []),
186
+ };
187
+ return {
188
+ forecast,
189
+ warnings: warningsOf(signals, readSnoozes(deps.db, now), now),
190
+ checkedAt: probes?.checkedAt ?? null,
191
+ weekly: weeklyReport(deps.db, new Date(now.valueOf() - 7 * dayMs).toISOString(), now.toISOString(), channelId),
192
+ weeklyNotify: weeklyNotifyOf(deps.db),
193
+ };
194
+ }
@@ -0,0 +1,85 @@
1
+ import { diskLowBytes, diskLowShare, fewTopicsLeft, lowPercent, } from "./model.js";
2
+ const gb = (bytes) => `${(bytes / 1024 ** 3).toFixed(1)} GB`;
3
+ const dollars = (value) => `$${value.toFixed(2)}`;
4
+ // Each warning says what is wrong, why it stops the autopilot, and where the fix is. A
5
+ // warning hidden with Hide stays hidden until its snooze ends.
6
+ export function warningsOf(signals, snoozes, now) {
7
+ const found = [];
8
+ for (const cli of signals.clis)
9
+ if (cli.login === "signed-out")
10
+ found.push({
11
+ key: `cli-signed-out:${cli.id}`,
12
+ severity: "problem",
13
+ text: `${cli.name} is signed out, so scheduled runs that use it will fail. Open a terminal on this computer, run "${cli.signIn}" and sign in.`,
14
+ fix: { kind: "settings", section: "providers", label: "Open Providers" },
15
+ });
16
+ for (const plan of signals.plans) {
17
+ const weekly = plan.weeklyPercent ?? 0;
18
+ const fiveHour = plan.fiveHourPercent ?? 0;
19
+ if (weekly < lowPercent && fiveHour < lowPercent)
20
+ continue;
21
+ const window = weekly >= fiveHour ? "weekly" : "5-hour";
22
+ found.push({
23
+ key: `plan-low:${plan.name}`,
24
+ severity: "warning",
25
+ text: `${plan.name} has used ${String(Math.round(Math.max(weekly, fiveHour)))}% of its ${window} limit, so scheduled runs on it may wait until the limit resets. Check when it resets, or move a template to another provider.`,
26
+ fix: { kind: "settings", section: "usage", label: "See plan usage" },
27
+ });
28
+ }
29
+ const key = signals.openRouter;
30
+ if (key !== null && key.limit > 0 && key.remaining / key.limit < 1 - lowPercent / 100)
31
+ found.push({
32
+ key: "openrouter-low",
33
+ severity: key.remaining <= 0 ? "problem" : "warning",
34
+ text: `Your OpenRouter key has ${dollars(Math.max(0, key.remaining))} left of its ${dollars(key.limit)} limit, so runs that use OpenRouter stop when it runs out. Raise the key's limit or add credit on openrouter.ai.`,
35
+ fix: { kind: "settings", section: "providers", label: "Open Providers" },
36
+ });
37
+ const disk = signals.disk;
38
+ if (disk !== null &&
39
+ (disk.freeBytes < diskLowBytes ||
40
+ (disk.totalBytes > 0 && disk.freeBytes / disk.totalBytes < diskLowShare)))
41
+ found.push({
42
+ key: "disk-low",
43
+ severity: disk.freeBytes < diskLowBytes ? "problem" : "warning",
44
+ text: `Only ${gb(disk.freeBytes)} is free on the disk Slopify saves to, so renders and downloads may fail. Free some space, or delete old versions and empty the Trash in Settings → Backup & storage.`,
45
+ fix: { kind: "settings", section: "storage", label: "Open Backup & storage" },
46
+ });
47
+ for (const schedule of signals.schedules) {
48
+ if (schedule.paused)
49
+ continue;
50
+ if (schedule.notReadyTemplate !== null)
51
+ found.push({
52
+ key: `template-not-ready:${schedule.id}`,
53
+ severity: "problem",
54
+ text: `“${schedule.name}” skips its runs because its template “${schedule.notReadyTemplate}” has no version approved for schedules. Open it from Templates with Edit template, then choose More → Approve for schedules.`,
55
+ fix: { kind: "templates", label: "Open Templates" },
56
+ });
57
+ if (schedule.topicsError !== null && schedule.generation !== "off")
58
+ found.push({
59
+ key: `topics-failing:${schedule.id}`,
60
+ severity: "problem",
61
+ text: `“${schedule.name}” couldn't find new topics: ${schedule.topicsError} Its queue won't refill until that is fixed. Open the schedule, fix what the error says, then find topics again.`,
62
+ fix: { kind: "schedule", scheduleId: schedule.id, label: "Open the schedule" },
63
+ });
64
+ if (schedule.generation === "off" && schedule.queued > 0 && schedule.queued < fewTopicsLeft)
65
+ found.push({
66
+ key: `few-topics:${schedule.id}:${String(schedule.queued)}`,
67
+ severity: "warning",
68
+ text: `“${schedule.name}” has ${schedule.queued === 1 ? "1 topic" : `${String(schedule.queued)} topics`} left and doesn't find new ones, so it stops after ${schedule.queued === 1 ? "its next run" : `its next ${String(schedule.queued)} runs`}. Add topics to its queue, or turn on topic generation.`,
69
+ fix: { kind: "schedule", scheduleId: schedule.id, label: "Open the schedule" },
70
+ });
71
+ }
72
+ for (const use of signals.retired)
73
+ found.push({
74
+ key: `retired:${use.kind}:${use.id}:${use.model}`,
75
+ severity: "warning",
76
+ text: `The ${use.slot.toLowerCase()} “${use.model}” in ${use.kind} “${use.name}” is ${use.why === "retired" ? "being retired" : "no longer offered"}, so its runs will fail once it is gone. Switch it to ${use.replacement === null ? "another model" : `“${use.replacement}”`}.`,
77
+ fix: use.kind === "schedule"
78
+ ? { kind: "schedule", scheduleId: use.id, label: "Open the schedule" }
79
+ : { kind: "templates", label: "Open Templates" },
80
+ });
81
+ const at = now.toISOString();
82
+ return found
83
+ .filter((warning) => (snoozes.get(warning.key) ?? "") <= at)
84
+ .toSorted((a, b) => Number(b.severity === "problem") - Number(a.severity === "problem"));
85
+ }
@@ -0,0 +1,100 @@
1
+ /// <reference lib="esnext.temporal" />
2
+ import { z } from "zod";
3
+ import { liveProject } from "../admission/repo.js";
4
+ import { defaultChannelId } from "../channels/model.js";
5
+ import { projectChannels } from "../channels/repo.js";
6
+ import { weekSummary } from "../run-cost/week.js";
7
+ import { validTimeZone } from "../schedules/calendar.js";
8
+ import { notTemplateSample } from "../template-studio/repo.js";
9
+ // ceiling: the report names this many failed steps; the rest are counted.
10
+ const failedShown = 10;
11
+ // The hour on Monday, in the person's time zone, from which the week's report is sent.
12
+ const sendHour = 9;
13
+ const stageNames = {
14
+ research: "Research",
15
+ article: "Article",
16
+ audio: "Narration",
17
+ images: "Images",
18
+ thumbnail: "Thumbnail",
19
+ video: "Video",
20
+ document: "Document",
21
+ };
22
+ const failedRow = z.object({
23
+ project_id: z.string(),
24
+ title: z.string(),
25
+ kind: z.string(),
26
+ failure_reason: z.string().nullable(),
27
+ });
28
+ // The last week in one read: what finished and what failed, what it cost and where the plans
29
+ // stand. The same stored calls Home's This week reads; nothing is estimated.
30
+ export function weeklyReport(db, since, until, channelId) {
31
+ const summary = weekSummary(db, since, channelId);
32
+ const channels = channelId === undefined ? undefined : projectChannels(db);
33
+ const failed = db
34
+ .prepare(`SELECT stages.project_id, projects.title, stages.kind, stages.failure_reason
35
+ FROM stages JOIN projects ON projects.id=stages.project_id
36
+ WHERE stages.state='failed' AND stages.finished_at>=? AND ${liveProject()}
37
+ AND ${notTemplateSample()}
38
+ ORDER BY stages.finished_at DESC, stages.id`)
39
+ .all(since)
40
+ .map((raw) => failedRow.parse(raw))
41
+ .filter((row) => channels === undefined || (channels.get(row.project_id) ?? defaultChannelId) === channelId)
42
+ .map((row) => ({
43
+ projectId: row.project_id,
44
+ title: row.title.trim() === "" ? "Untitled project" : row.title,
45
+ stage: stageNames[row.kind] ?? row.kind,
46
+ reason: row.failure_reason ?? "It stopped without a reason.",
47
+ }));
48
+ return {
49
+ since,
50
+ until,
51
+ finished: summary.videos,
52
+ failed: failed.slice(0, failedShown),
53
+ failedMore: Math.max(0, failed.length - failedShown),
54
+ cost: summary.cost,
55
+ unpriced: summary.unpriced,
56
+ apiEquivalent: summary.apiEquivalent,
57
+ plans: summary.plans.map((plan) => ({
58
+ name: plan.name,
59
+ weeklyPercent: plan.weeklyPercent,
60
+ fiveHourPercent: plan.fiveHourPercent,
61
+ })),
62
+ };
63
+ }
64
+ // The ISO week a moment falls in, in the person's time zone: "2026-W42".
65
+ export function isoWeekOf(now, timeZone) {
66
+ const zone = validTimeZone(timeZone) ? timeZone : "UTC";
67
+ const date = Temporal.Instant.fromEpochMilliseconds(now.valueOf()).toZonedDateTimeISO(zone);
68
+ return `${String(date.yearOfWeek)}-W${String(date.weekOfYear).padStart(2, "0")}`;
69
+ }
70
+ // Whether this week's report is still to be sent: from Monday 09:00 in the person's time zone,
71
+ // once a week. A Slopify that was off on Monday sends it when it next runs that week.
72
+ export function weeklyDue(now, timeZone, sentFor) {
73
+ const zone = validTimeZone(timeZone) ? timeZone : "UTC";
74
+ const date = Temporal.Instant.fromEpochMilliseconds(now.valueOf()).toZonedDateTimeISO(zone);
75
+ const week = isoWeekOf(now, zone);
76
+ const started = date.dayOfWeek > 1 || date.hour >= sendHour;
77
+ return { due: started && sentFor !== week, week };
78
+ }
79
+ const money = (value) => `$${value.toFixed(2)}`;
80
+ export function weeklyNoticeText(report) {
81
+ const failures = report.failed.length + report.failedMore;
82
+ const plans = report.plans
83
+ .filter((plan) => plan.weeklyPercent !== null)
84
+ .map((plan) => `${plan.name} ${String(Math.round(plan.weeklyPercent ?? 0))}% of its week`);
85
+ const named = report.failed
86
+ .slice(0, 3)
87
+ .map((one) => `“${one.title}” (${one.stage}): ${one.reason.replace(/[.!?\s]+$/, "")}`);
88
+ return {
89
+ headline: `Slopify last week: ${report.finished === 1 ? "1 video" : `${String(report.finished)} videos`} made, ${failures === 1 ? "1 step" : `${String(failures)} steps`} failed`,
90
+ detail: [
91
+ `Cost ${money(report.cost)}${report.unpriced > 0 ? ` plus ${String(report.unpriced)} calls with no known price` : ""}.`,
92
+ plans.length === 0 ? "" : `Plans: ${plans.join(", ")}.`,
93
+ named.length === 0
94
+ ? ""
95
+ : `Failed: ${named.join("; ")}${failures > named.length ? `; and ${String(failures - named.length)} more` : ""}. Open each project and use the button beside the failed step.`,
96
+ ]
97
+ .filter((line) => line !== "")
98
+ .join(" "),
99
+ };
100
+ }
@@ -1,6 +1,7 @@
1
1
  import { constants } from "node:fs";
2
2
  import { lstat, mkdir, open, readdir, rename, rm } from "node:fs/promises";
3
3
  import { join } from "node:path";
4
+ import { errnoCode } from "../../kernel/errors.js";
4
5
  // Only files whose whole name matches these are this feature's. Pruning and the leftover sweep
5
6
  // touch nothing else in the folder: not an Export everything download
6
7
  // (slopify-backup-2026-09-27.tar), not a file the user renamed, not a symlink.
@@ -92,7 +93,7 @@ export async function listBackups(folder) {
92
93
  names = await readdir(folder);
93
94
  }
94
95
  catch (error) {
95
- if (codeOf(error) === "ENOENT")
96
+ if (errnoCode(error) === "ENOENT")
96
97
  return [];
97
98
  throw error;
98
99
  }
@@ -128,8 +129,3 @@ export async function removeLeftovers(folder) {
128
129
  }
129
130
  return removed;
130
131
  }
131
- export function codeOf(error) {
132
- return error instanceof Error && "code" in error && typeof error.code === "string"
133
- ? error.code
134
- : undefined;
135
- }
@@ -1,28 +1,16 @@
1
- import { readSetting, writeSetting } from "../settings/repo.js";
1
+ import { readJsonSetting, writeSetting } from "../settings/repo.js";
2
2
  import { backupStatusSchema, configKey, defaultConfig, emptyStatus, statusKey, storedConfigSchema, } from "./model.js";
3
3
  // A value this build cannot read (hand-edited, or written by a newer Slopify) falls back to
4
4
  // the default rather than stopping the app; the next save writes a good one.
5
5
  export function readConfig(db, fallbackTimeZone) {
6
- const parsed = storedConfigSchema.safeParse(parseJson(readSetting(db, configKey)));
7
- return parsed.success ? parsed.data : defaultConfig(fallbackTimeZone);
6
+ return readJsonSetting(db, configKey, storedConfigSchema, defaultConfig(fallbackTimeZone));
8
7
  }
9
8
  export function writeConfig(db, config) {
10
9
  writeSetting(db, configKey, JSON.stringify(storedConfigSchema.parse(config)));
11
10
  }
12
11
  export function readStatus(db) {
13
- const parsed = backupStatusSchema.safeParse(parseJson(readSetting(db, statusKey)));
14
- return parsed.success ? parsed.data : emptyStatus;
12
+ return readJsonSetting(db, statusKey, backupStatusSchema, emptyStatus);
15
13
  }
16
14
  export function writeStatus(db, status) {
17
15
  writeSetting(db, statusKey, JSON.stringify(backupStatusSchema.parse(status)));
18
16
  }
19
- function parseJson(value) {
20
- if (value === undefined)
21
- return undefined;
22
- try {
23
- return JSON.parse(value);
24
- }
25
- catch {
26
- return undefined;
27
- }
28
- }
@@ -1,7 +1,8 @@
1
1
  import { statfs } from "node:fs/promises";
2
+ import { errnoCode } from "../../kernel/errors.js";
2
3
  import { BackupBusyError, planBackup, streamBackup, } from "../storage/backup-export.js";
3
4
  import { defaultBackupsDir } from "../storage/layout.js";
4
- import { backupFileName, codeOf, ensureFolder, listBackups, pruneBackups, removeLeftovers, writeAtomically, } from "./files.js";
5
+ import { backupFileName, ensureFolder, listBackups, pruneBackups, removeLeftovers, writeAtomically, } from "./files.js";
5
6
  import { effectiveFolder, folderProblem, hostFolder } from "./folder.js";
6
7
  import { readConfig, readStatus, writeConfig, writeStatus } from "./repo.js";
7
8
  import { decideBackup, nextSlot } from "./schedule.js";
@@ -59,7 +60,7 @@ export function createBackupService(deps) {
59
60
  }
60
61
  catch (error) {
61
62
  deps.log.write("warn", "backups.prune", { detail: String(error) });
62
- detail = `The backup was saved, but older backups in ${folder} could not be removed (${codeOf(error) ?? "unknown error"}), so more than ${settings.keep} are kept. Check Slopify can delete files in that folder, or pick another Folder in Settings → Backups.`;
63
+ detail = `The backup was saved, but older backups in ${folder} could not be removed (${errnoCode(error) ?? "unknown error"}), so more than ${settings.keep} are kept. Check Slopify can delete files in that folder, or pick another Folder in Settings → Backup & storage.`;
63
64
  }
64
65
  record({
65
66
  ...attempt,
@@ -272,17 +273,17 @@ function named(projects) {
272
273
  .join(", ");
273
274
  return projects.length > 3 ? `${titles} and ${projects.length - 3} more` : titles;
274
275
  }
275
- // What failed, why, and what to change - the sentence Settings → Backups shows.
276
+ // What failed, why, and what to change - the sentence Settings → Backup & storage shows.
276
277
  export function explain(error, folder, retries, signal) {
277
- const fix = "Pick another Folder in Settings → Backups, or leave it empty to use the default";
278
+ const fix = "Pick another Folder in Settings → Backup & storage, or leave it empty to use the default";
278
279
  if (signal?.aborted === true && error === signal.reason)
279
280
  return "The backup was not saved: Slopify stopped while writing it, and the unfinished file was removed. It runs again after Slopify starts.";
280
281
  if (error instanceof NoSpaceError)
281
- return `The backup was not saved: it needs about ${bytes(error.needed)}, but the disk holding ${folder} has ${bytes(error.free)} free. Free up space there, lower "Keep last" in Settings → Backups, or pick another Folder.`;
282
- switch (codeOf(error)) {
282
+ return `The backup was not saved: it needs about ${bytes(error.needed)}, but the disk holding ${folder} has ${bytes(error.free)} free. Free up space there, lower "Keep last" in Settings → Backup & storage, or pick another Folder.`;
283
+ switch (errnoCode(error)) {
283
284
  case "ENOSPC":
284
285
  case "EDQUOT":
285
- return `The backup was not saved: the disk holding ${folder} ran out of space. Free up space there, lower "Keep last" in Settings → Backups, or pick another Folder.`;
286
+ return `The backup was not saved: the disk holding ${folder} ran out of space. Free up space there, lower "Keep last" in Settings → Backup & storage, or pick another Folder.`;
286
287
  case "EACCES":
287
288
  case "EPERM":
288
289
  case "EROFS":
@@ -1,3 +1,4 @@
1
+ import { errorText } from "../../kernel/errors.js";
1
2
  import { castImageMaxBytes, castImagesPerMember, sniffImage, storeImageBlob } from "./images.js";
2
3
  import { castMemberById } from "./repo.js";
3
4
  import { castGenerateSchema } from "./schema.js";
@@ -58,9 +59,9 @@ export function generateCastImage(deps, memberId, input) {
58
59
  .prepare("UPDATE cast_images SET state='ready',sha256=? WHERE id=? AND state='generating'")
59
60
  .run(sha256, id);
60
61
  }, (error) => {
61
- const reason = error instanceof Error ? error.message : String(error);
62
+ const reason = errorText(error);
62
63
  deps.log?.write("warn", "channels.cast-image.failed", { detail: reason.slice(0, 500) });
63
- fail(deps, id, `The picture couldn't be made: ${reason.slice(0, 500)} Check the image provider in Settings → Providers, then press Generate again.`);
64
+ fail(deps, id, `The picture couldn't be made: ${reason.slice(0, 500)} Check the image service in Settings → Keys & services, then press Make the picture again.`);
64
65
  });
65
66
  return { ok: true, value: imageRow(deps, id) };
66
67
  }
@@ -1,5 +1,4 @@
1
1
  // Every install has this channel (migration 0032). A template or project whose channel is
2
2
  // NULL, or names a channel this install does not have, belongs to it.
3
3
  export const defaultChannelId = "00000000-0000-4000-8000-000000000001";
4
- export const defaultChannelName = "My channel";
5
4
  export const castKinds = ["character", "creature", "place", "object"];
@@ -8,9 +8,6 @@ import { defaultDocumentTheme } from "../document/model.js";
8
8
  import { entryByName } from "../library/repo.js";
9
9
  import { channelById, resolveChannelId } from "./repo.js";
10
10
  import { brandDocument, castSnapshot } from "./runs.js";
11
- export function channelChanged(db, before, after) {
12
- return resolveChannelId(db, before.channelId) !== resolveChannelId(db, after.channelId);
13
- }
14
11
  export function rebrandedEdit(db, base, edit) {
15
12
  const oldId = resolveChannelId(db, base.channelId);
16
13
  const newId = resolveChannelId(db, edit.config.channelId);
@@ -1,4 +1,5 @@
1
1
  import { z } from "zod";
2
+ import { channelSocialOf } from "../social/channel.js";
2
3
  import { aiDisclosureSettings } from "../studio/disclosure.js";
3
4
  import { channelAmbientBedSchema } from "../video/ambient-bed-schema.js";
4
5
  import { castKinds, defaultChannelId, } from "./model.js";
@@ -25,6 +26,8 @@ const channelRow = z.object({
25
26
  brand_json: z.string(),
26
27
  series_brief: z.string(),
27
28
  ai_disclosure: z.enum(aiDisclosureSettings),
29
+ x_tier: z.string().optional(),
30
+ social_accounts: z.string().optional(),
28
31
  version: z.number(),
29
32
  created_at: z.string(),
30
33
  updated_at: z.string(),
@@ -69,6 +72,7 @@ function channelOf(row) {
69
72
  brand: brandSchema.parse(JSON.parse(value.brand_json)),
70
73
  seriesBrief: value.series_brief,
71
74
  aiDisclosure: value.ai_disclosure,
75
+ ...((social) => ({ xTier: social.xTier, socialAccounts: social.accounts }))(channelSocialOf(value.x_tier, value.social_accounts)),
72
76
  version: value.version,
73
77
  createdAt: value.created_at,
74
78
  updatedAt: value.updated_at,
@@ -1,5 +1,6 @@
1
1
  import { z } from "zod";
2
2
  import { languageSchema } from "../../kernel/ports/languages.js";
3
+ import { socialPlatforms, xTiers } from "../social/model.js";
3
4
  import { aiDisclosureSettings } from "../studio/disclosure.js";
4
5
  import { ambientBedProblems } from "../video/ambient-bed.js";
5
6
  import { channelAmbientBedSchema } from "../video/ambient-bed-schema.js";
@@ -9,7 +10,7 @@ import { castKinds } from "./model.js";
9
10
  const id = z.uuid();
10
11
  const colour = z
11
12
  .string()
12
- .regex(/^#[0-9a-fA-F]{6}$/, "Use a colour like #FFD700.")
13
+ .regex(/^#[0-9a-fA-F]{6}$/, "Use a color like #FFD700.")
13
14
  .transform((value) => value.toUpperCase());
14
15
  const optionalText = (max) => z.string().trim().max(max).optional();
15
16
  const fontId = z
@@ -75,6 +76,11 @@ export const channelUpdateSchema = z
75
76
  name,
76
77
  brand: brandKitSchema,
77
78
  seriesBrief: z.string().max(10000, "Keep the series brief to 10,000 characters or fewer."),
79
+ // Brand → Posting (`slices/social`): left out, they stay as they are.
80
+ xTier: z.enum(xTiers, "Choose Free, Premium or Premium+.").optional(),
81
+ socialAccounts: z
82
+ .partialRecord(z.enum(socialPlatforms), z.string().max(260, "Keep each account name to 260 characters or fewer."))
83
+ .optional(),
78
84
  baseVersion: z.number().int().positive(),
79
85
  })
80
86
  .strict()
@@ -1,4 +1,5 @@
1
1
  import { transact } from "../../kernel/db/tx.js";
2
+ import { accountsProblem, writeChannelSocial } from "../social/channel.js";
2
3
  import { defaultChannelId } from "./model.js";
3
4
  import { castMemberById, castOfChannel, channelById, channelSummaries } from "./repo.js";
4
5
  import { castMemberCreateSchema, castMemberUpdateSchema, castMoveSchema, channelAiDisclosureSchema, channelCreateSchema, channelUpdateSchema, templateChannelSchema, } from "./schema.js";
@@ -50,9 +51,18 @@ export function updateChannel(deps, id, input) {
50
51
  if (previous.version !== parsed.data.baseVersion)
51
52
  return { ok: false, reason: "conflict" };
52
53
  const brand = parsed.data.brand;
54
+ const accountsWrong = parsed.data.socialAccounts === undefined
55
+ ? undefined
56
+ : accountsProblem(parsed.data.socialAccounts);
57
+ if (accountsWrong !== undefined)
58
+ return { ok: false, reason: "invalid-input", message: accountsWrong };
53
59
  deps.db
54
60
  .prepare("UPDATE channels SET name=?,brand_json=?,series_brief=?,version=version+1,updated_at=? WHERE id=? AND version=?")
55
61
  .run(parsed.data.name, JSON.stringify(brand), parsed.data.seriesBrief, deps.clock.now().toISOString(), id, parsed.data.baseVersion);
62
+ writeChannelSocial(deps.db, id, {
63
+ xTier: parsed.data.xTier,
64
+ accounts: parsed.data.socialAccounts,
65
+ });
56
66
  const saved = channelById(deps.db, id);
57
67
  if (saved === undefined)
58
68
  throw new Error("The saved channel could not be read back");
@@ -1,6 +1,6 @@
1
1
  import { z } from "zod";
2
2
  import { transact } from "../../kernel/db/tx.js";
3
- import { readSetting, writeSetting } from "../settings/repo.js";
3
+ import { readJsonSetting, writeSetting } from "../settings/repo.js";
4
4
  import { channelById } from "./repo.js";
5
5
  import { titlesFromCsv, titlesFromLines } from "./studio-csv.js";
6
6
  export const videoTitleMax = 500;
@@ -17,7 +17,7 @@ const importSchema = z
17
17
  // The preview's "Keep only titles containing…" text, remembered for the next import.
18
18
  filter: z
19
19
  .string()
20
- .max(importFilterMax, `The filter is too long to remember. Shorten "Keep only titles containing…" on the channel's Existing videos tab to at most ${importFilterMax} characters.`)
20
+ .max(importFilterMax, `The filter is too long to remember. Shorten "Keep only titles containing…" on the channel's Titles to avoid tab to at most ${importFilterMax} characters.`)
21
21
  .optional(),
22
22
  })
23
23
  .strict();
@@ -47,7 +47,7 @@ export function previewChannelVideos(deps, channelId, input) {
47
47
  const read = importOf(input);
48
48
  if (!read.ok)
49
49
  return read;
50
- const filter = storedFilter(readSetting(deps.db, filterKey(channelId)));
50
+ const filter = readJsonSetting(deps.db, filterKey(channelId), z.string(), "");
51
51
  return { ok: true, value: { titles: read.value.titles, filter } };
52
52
  }
53
53
  // Titles already on the channel (any case) and repeats within the import are skipped.
@@ -69,18 +69,6 @@ export function importChannelVideos(deps, channelId, input) {
69
69
  return { ok: true, value: { added, skipped: titles.length - added } };
70
70
  });
71
71
  }
72
- // Stored as JSON, like every row of the `settings` table a backup carries.
73
- function storedFilter(stored) {
74
- if (stored === undefined)
75
- return "";
76
- try {
77
- const value = JSON.parse(stored);
78
- return typeof value === "string" ? value : "";
79
- }
80
- catch {
81
- return "";
82
- }
83
- }
84
72
  function importOf(input) {
85
73
  const parsed = importSchema.safeParse(input);
86
74
  if (!parsed.success)
@@ -1,2 +1 @@
1
1
  export { approveCheckpoint, checkpointForWork, listCheckpoints, saveCheckpointSet, settleCheckpointClosures, } from "./repo.js";
2
- export { checkpointApprovalSchema, checkpointRowSchema, checkpointSetSchema, checkpointStageSchema, checkpointStateSchema, } from "./schema.js";