@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
@@ -1,6 +1,6 @@
1
1
  import { randomBytes, timingSafeEqual } from "node:crypto";
2
2
  import { z } from "zod";
3
- import { readSetting, writeSetting } from "../settings/repo.js";
3
+ import { deleteSetting, readJsonSetting, stringListSchema, writeSetting, } from "../settings/repo.js";
4
4
  import { playlistUses, studioPlaylistMax, } from "./model.js";
5
5
  // Rows of the key/value `settings` table. The playlists travel with a backup; the
6
6
  // pairing stays on this machine (`slices/storage/portable.ts`): its token is what lets the
@@ -14,7 +14,7 @@ const pairingSchema = z.object({
14
14
  });
15
15
  // A channel's own playlists are a row of their own, `studio.playlist.<channelId>`; a channel
16
16
  // without one uses the default list above. Each playlist is ticked by default or not; a
17
- // project's Prepare upload can change which it goes into. A row saved before lists existed
17
+ // project's Copy for YouTube Studio can change which it goes into. A row saved before lists existed
18
18
  // holds one name, read as one playlist ticked by default.
19
19
  export const studioChannelPlaylistPrefix = `${studioPlaylistKey}.`;
20
20
  function playlistKey(channelId) {
@@ -58,31 +58,23 @@ export function readChannelPlaylists(db) {
58
58
  return out;
59
59
  }
60
60
  function storedPlaylists(db, key) {
61
- const stored = readSetting(db, key);
62
- if (stored === undefined)
61
+ const stored = readJsonSetting(db, key, storedPlaylistsSchema, null);
62
+ if (stored === null)
63
63
  return null;
64
- try {
65
- const parsed = storedPlaylistsSchema.safeParse(JSON.parse(stored));
66
- if (!parsed.success)
67
- return null;
68
- if (typeof parsed.data === "string")
69
- return parsed.data.trim() === "" ? null : [{ name: parsed.data.trim(), byDefault: true }];
70
- return parsed.data.length === 0 ? null : parsed.data;
71
- }
72
- catch {
73
- return null;
74
- }
64
+ if (typeof stored === "string")
65
+ return stored.trim() === "" ? null : [{ name: stored.trim(), byDefault: true }];
66
+ return stored.length === 0 ? null : stored;
75
67
  }
76
- // Why a list can't be saved, in words for Settings → YouTube Studio; undefined when it can.
68
+ // Why a list can't be saved, in words for Settings → YouTube Studio and posting; undefined when it can.
77
69
  export function studioPlaylistsProblem(list) {
78
70
  const long = list.find((one) => one.name.trim().length > studioPlaylistMax);
79
71
  if (long !== undefined)
80
- return `The playlist name "${long.name.slice(0, 40)}…" is longer than YouTube allows (${String(studioPlaylistMax)} characters). Shorten it in Settings → YouTube Studio.`;
72
+ return `The playlist name "${long.name.slice(0, 40)}…" is longer than YouTube allows (${String(studioPlaylistMax)} characters). Shorten it in Settings → YouTube Studio and posting.`;
81
73
  const seen = new Set();
82
74
  for (const one of list) {
83
75
  const key = one.name.trim().toLowerCase();
84
76
  if (seen.has(key))
85
- return `"${one.name.trim()}" is listed twice. Remove one in Settings → YouTube Studio.`;
77
+ return `"${one.name.trim()}" is listed twice. Remove one in Settings → YouTube Studio and posting.`;
86
78
  seen.add(key);
87
79
  }
88
80
  return undefined;
@@ -100,27 +92,18 @@ export function saveStudioPlaylists(db, list, channelId) {
100
92
  .filter((one) => one.name !== "");
101
93
  const key = playlistKey(channelId);
102
94
  if (value.length === 0) {
103
- db.prepare("DELETE FROM settings WHERE key = ?").run(key);
95
+ deleteSetting(db, key);
104
96
  return [];
105
97
  }
106
98
  writeSetting(db, key, JSON.stringify(value));
107
99
  return value;
108
100
  }
109
- // The playlists a project's uploads go into, when its Prepare upload changed them from the
101
+ // The playlists a project's uploads go into, when its Copy for YouTube Studio changed them from the
110
102
  // channel's defaults: one row, project id to names.
111
103
  export const studioProjectPlaylistsKey = "studio.projectPlaylists";
112
104
  export const storedProjectPlaylistsSchema = z.record(z.string().max(64), z.array(z.string().max(studioPlaylistMax)).max(studioPlaylistsMax));
113
105
  function projectChoices(db) {
114
- const stored = readSetting(db, studioProjectPlaylistsKey);
115
- if (stored === undefined)
116
- return {};
117
- try {
118
- const parsed = storedProjectPlaylistsSchema.safeParse(JSON.parse(stored));
119
- return parsed.success ? parsed.data : {};
120
- }
121
- catch {
122
- return {};
123
- }
106
+ return readJsonSetting(db, studioProjectPlaylistsKey, storedProjectPlaylistsSchema, {});
124
107
  }
125
108
  // The channel's playlists, each ticked as this project goes into it: the project's own choice
126
109
  // when it made one (a playlist the channel no longer lists is dropped), else the defaults.
@@ -139,29 +122,19 @@ export function saveProjectPlaylists(db, projectId, names) {
139
122
  const { [projectId]: _previous, ...others } = projectChoices(db);
140
123
  const next = names === null ? others : { ...others, [projectId]: [...names] };
141
124
  if (Object.keys(next).length === 0)
142
- db.prepare("DELETE FROM settings WHERE key = ?").run(studioProjectPlaylistsKey);
125
+ deleteSetting(db, studioProjectPlaylistsKey);
143
126
  else
144
127
  writeSetting(db, studioProjectPlaylistsKey, JSON.stringify(next));
145
128
  }
146
129
  // The projects whose uploaded clips are real footage (filmed, not generated): ticked in the
147
- // project's Prepare upload, under AI use. One row holding their ids; YouTube's second AI use
130
+ // project's Copy for YouTube Studio, under AI use. One row holding their ids; YouTube's second AI use
148
131
  // case is footage of a real event or place altered by AI (`disclosure.ts`).
149
132
  export const studioRealFootageKey = "studio.realFootage";
150
133
  // "on" or "off": whether the extension posts and pins each video's comment once it is public.
151
134
  export const autoCommentKey = "studio.autoComment";
152
135
  function realFootageIds(db) {
153
- const stored = readSetting(db, studioRealFootageKey);
154
- if (stored === undefined)
155
- return [];
156
- try {
157
- const value = JSON.parse(stored);
158
- return Array.isArray(value)
159
- ? value.filter((one) => typeof one === "string")
160
- : [];
161
- }
162
- catch {
163
- return [];
164
- }
136
+ const value = readJsonSetting(db, studioRealFootageKey, stringListSchema, []);
137
+ return value;
165
138
  }
166
139
  export function readRealFootage(db, projectId) {
167
140
  return realFootageIds(db).includes(projectId);
@@ -170,25 +143,15 @@ export function saveRealFootage(db, projectId, on) {
170
143
  const others = realFootageIds(db).filter((one) => one !== projectId);
171
144
  const next = on ? [...others, projectId].sort() : others;
172
145
  if (next.length === 0)
173
- db.prepare("DELETE FROM settings WHERE key = ?").run(studioRealFootageKey);
146
+ deleteSetting(db, studioRealFootageKey);
174
147
  else
175
148
  writeSetting(db, studioRealFootageKey, JSON.stringify(next));
176
149
  }
177
150
  // The pairing token, made on first read. One per install: a new one (Settings → YouTube
178
151
  // Studio → New pairing token) forgets the extension that used the old one.
179
152
  export function studioPairing(db) {
180
- const stored = readSetting(db, studioPairingKey);
181
- if (stored !== undefined) {
182
- try {
183
- const parsed = pairingSchema.safeParse(JSON.parse(stored));
184
- if (parsed.success)
185
- return parsed.data;
186
- }
187
- catch {
188
- // A damaged row is replaced below.
189
- }
190
- }
191
- return resetStudioPairing(db);
153
+ // A damaged row is replaced.
154
+ return readJsonSetting(db, studioPairingKey, pairingSchema, undefined) ?? resetStudioPairing(db);
192
155
  }
193
156
  // What is waiting to be filled in Studio is kept per pairing (`queue.ts`): one row,
194
157
  // `studio.fillQueue.<token hash>`, so a new token starts with nothing waiting.
@@ -1,3 +1,4 @@
1
+ import { markUploaded } from "../uploads/repo.js";
1
2
  const taskState = (value) => value === "waiting" || value === "done" || value === "failed" ? value : "none";
2
3
  // A YouTube video id: 11 letters, digits, `-` and `_`.
3
4
  export const videoIdPattern = /^[A-Za-z0-9_-]{11}$/;
@@ -41,15 +42,33 @@ export function recordVideo(db, projectId, short, videoId, at, state = "done") {
41
42
  upload_state=CASE WHEN video_id=excluded.video_id AND upload_state='done' THEN 'done'
42
43
  ELSE excluded.upload_state END,
43
44
  video_id=excluded.video_id, recorded_at=excluded.recorded_at`).run(projectId, slot(short), videoId, at, state);
45
+ if (short === null && videoOf(db, projectId, null)?.uploadState === "done")
46
+ markUploaded(db, projectId, true, at);
44
47
  }
45
48
  // Studio said the upload was scheduled or published. False when no such upload was filled.
46
- export function confirmUpload(db, projectId, short, videoId) {
47
- return (db
49
+ export function confirmUpload(db, projectId, short, videoId, at) {
50
+ const confirmed = db
48
51
  .prepare("UPDATE youtube_videos SET upload_state='done' WHERE project_id=? AND short=? AND video_id=?")
49
- .run(projectId, slot(short), videoId).changes > 0);
52
+ .run(projectId, slot(short), videoId).changes > 0;
53
+ if (confirmed && short === null)
54
+ markUploaded(db, projectId, true, at);
55
+ return confirmed;
50
56
  }
51
- export function forgetVideo(db, projectId, short) {
57
+ // Forgets the video an upload became (deleted on YouTube, or a wrong link). Forgetting a long
58
+ // video that was on YouTube takes the project's uploaded mark off with it; an upload only
59
+ // filled in Studio never set the mark, so forgetting it leaves a mark made by hand.
60
+ export function forgetVideo(db, projectId, short, at) {
61
+ const was = videoOf(db, projectId, short);
52
62
  db.prepare("DELETE FROM youtube_videos WHERE project_id=? AND short=?").run(projectId, slot(short));
63
+ if (short === null && was?.uploadState === "done")
64
+ markUploaded(db, projectId, false, at);
65
+ }
66
+ // "Mark not uploaded" (Home, Projects): the long video's link goes with the mark, so the two
67
+ // never disagree. An upload only filled in Studio is not on YouTube and stays.
68
+ export function markNotUploaded(db, projectId, at) {
69
+ if (videoOf(db, projectId, null)?.uploadState === "done")
70
+ forgetVideo(db, projectId, null, at);
71
+ return markUploaded(db, projectId, false, at);
53
72
  }
54
73
  export function projectVideos(db, projectId) {
55
74
  return db
@@ -63,6 +82,18 @@ export function videoOf(db, projectId, short) {
63
82
  .get(projectId, slot(short));
64
83
  return row === undefined ? undefined : rowOf(row);
65
84
  }
85
+ // What the extension reported after setting up Studio's A/B Testing on the video's Details page,
86
+ // asked for from On YouTube or its popup: "started" once filled (the person presses Set test),
87
+ // "failed" with its reason. A failed later attempt keeps a test already set up ("started"), so
88
+ // its result is still read. False when the project has no video recorded for that upload.
89
+ export function setAbState(db, projectId, short, state, message, at) {
90
+ return (db
91
+ .prepare(`UPDATE youtube_videos SET
92
+ ab_state=CASE WHEN ?='failed' AND ab_state='started' THEN 'started' ELSE ? END,
93
+ ab_message=?, ab_at=?
94
+ WHERE project_id=? AND short=?`)
95
+ .run(state, state, message, at, projectId, slot(short)).changes > 0);
96
+ }
66
97
  export function setTaskState(db, task, projectId, short, state, message) {
67
98
  const column = task === "finish" ? "finish" : "comment";
68
99
  db.prepare(`UPDATE youtube_videos SET ${column}_state=?, ${column}_message=? WHERE project_id=? AND short=?`).run(state, message, projectId, slot(short));
@@ -1,5 +1,6 @@
1
1
  import { copyFileSync, mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
2
2
  import { join } from "node:path";
3
+ import { errorText } from "../../kernel/errors.js";
3
4
  import { defaultMotionStyle, defaultZoomPercent } from "../admission/rules.js";
4
5
  import { isMissingFont, resolveBoldFont, resolveFont } from "../fonts/index.js";
5
6
  import { renderShort } from "../shorts/render.js";
@@ -9,7 +10,7 @@ import { planRender } from "../video/plan.js";
9
10
  import { renderSlideshow } from "../video/slideshow.js";
10
11
  import { bundledSampleAssets, sampleNarrationSeconds } from "./narration.js";
11
12
  import { stylePreviewSeconds } from "./schema.js";
12
- import { previewCues, previewWords } from "./settings.js";
13
+ import { previewCues, previewShots, previewWords } from "./settings.js";
13
14
  export class StylePreviewError extends Error {
14
15
  reason;
15
16
  constructor(reason, message) {
@@ -25,7 +26,6 @@ const previewFrame = {
25
26
  "9:16": { width: 270, height: 480 },
26
27
  "1:1": { width: 360, height: 360 },
27
28
  };
28
- const shotSeconds = stylePreviewSeconds / 3;
29
29
  const chapterTitle = "Chapter one";
30
30
  export function ffmpegStylePreview(deps) {
31
31
  const assets = deps.assets ?? bundledSampleAssets();
@@ -33,25 +33,32 @@ export function ffmpegStylePreview(deps) {
33
33
  try {
34
34
  // A real picture is shown for all three shots: the motion, the Look and the captions read
35
35
  // on it, and the transition and chapter card still show at the cuts.
36
- const images = picture !== undefined
36
+ const stills = picture !== undefined
37
37
  ? [picture, picture, picture]
38
38
  : settings.format === "9:16"
39
39
  ? assets.tall
40
40
  : assets.wide;
41
+ // A longer preview shows the stills again in turn.
42
+ const { shots } = previewShots(settings);
43
+ const images = settings.short !== undefined
44
+ ? stills
45
+ : Array.from({ length: shots }, (_, index) => stills[index % stills.length] ?? "");
46
+ const machine = await deps.machine?.();
41
47
  if (settings.short !== undefined)
42
- await renderShortPreview(deps, settings, settings.short, assets, images, output, signal);
48
+ await renderShortPreview(deps, settings, settings.short, assets, images, output, signal, machine);
43
49
  else
44
- await renderPreview(deps, settings, assets, images, output, signal);
50
+ await renderPreview(deps, settings, assets, images, output, signal, machine);
45
51
  }
46
52
  catch (error) {
47
53
  throw explained(deps.log, error);
48
54
  }
49
55
  };
50
56
  }
51
- async function renderPreview(deps, settings, assets, images, output, signal) {
57
+ async function renderPreview(deps, settings, assets, images, output, signal, machine) {
52
58
  mkdirSync(deps.dir, { recursive: true, mode: 0o700 });
53
59
  const workspace = mkdtempSync(join(deps.dir, "work-"));
54
60
  try {
61
+ const { shotSeconds } = previewShots(settings);
55
62
  const cardFont = settings.chapterCard === null
56
63
  ? undefined
57
64
  : await resolveFont(deps.paths, settings.chapterCard.fontId);
@@ -60,8 +67,8 @@ async function renderPreview(deps, settings, assets, images, output, signal) {
60
67
  gapSeconds: 0,
61
68
  edgeSeconds: 0,
62
69
  imageSeconds: shotSeconds,
63
- zoomPercent: defaultZoomPercent,
64
- motionStyle: defaultMotionStyle,
70
+ zoomPercent: settings.timing?.zoomPercent ?? defaultZoomPercent,
71
+ motionStyle: settings.timing?.motionStyle ?? defaultMotionStyle,
65
72
  images,
66
73
  output,
67
74
  edit: {
@@ -81,14 +88,14 @@ async function renderPreview(deps, settings, assets, images, output, signal) {
81
88
  const edit = {
82
89
  ...plan.editList,
83
90
  ...previewFrame[settings.format],
84
- audio: [{ kind: "body", path: assets.narration, seconds: plan.totalSeconds }],
91
+ audio: narrationFor(assets.narration, plan.totalSeconds),
85
92
  };
86
93
  const captions = settings.captions;
87
94
  if (captions !== null) {
88
95
  const font = await resolveFont(deps.paths, captions.fontId);
89
96
  mkdirSync(join(workspace, "fonts"), { mode: 0o700 });
90
97
  copyFileSync(font.path, join(workspace, "fonts", `selected${font.extension}`));
91
- writeFileSync(join(workspace, "subtitles.ass"), serializeAss(previewCues(captions.text), {
98
+ writeFileSync(join(workspace, "subtitles.ass"), serializeAss(previewCues(captions.text, plan.totalSeconds), {
92
99
  ...subtitleFrame(settings.format),
93
100
  fontName: font.assName,
94
101
  fontSize: captions.fontSize,
@@ -105,6 +112,7 @@ async function renderPreview(deps, settings, assets, images, output, signal) {
105
112
  signal,
106
113
  log: deps.log,
107
114
  onProgress: () => { },
115
+ machine,
108
116
  });
109
117
  }
110
118
  finally {
@@ -113,7 +121,7 @@ async function renderPreview(deps, settings, assets, images, output, signal) {
113
121
  }
114
122
  // The Shorts layout, drawn at the preview's size: the same audio cut, image timing, captions
115
123
  // and headline a short gets, from the sample narration.
116
- async function renderShortPreview(deps, settings, short, assets, images, output, signal) {
124
+ async function renderShortPreview(deps, settings, short, assets, images, output, signal, machine) {
117
125
  mkdirSync(deps.dir, { recursive: true, mode: 0o700 });
118
126
  await renderShort({
119
127
  bin: deps.ffmpeg,
@@ -121,7 +129,7 @@ async function renderShortPreview(deps, settings, short, assets, images, output,
121
129
  start: 0,
122
130
  end: sampleNarrationSeconds,
123
131
  images,
124
- imageSeconds: shotSeconds,
132
+ imageSeconds: stylePreviewSeconds / 3,
125
133
  motionStyle: defaultMotionStyle,
126
134
  zoomPercent: defaultZoomPercent,
127
135
  words: previewWords(short.text),
@@ -134,16 +142,29 @@ async function renderShortPreview(deps, settings, short, assets, images, output,
134
142
  signal,
135
143
  log: deps.log,
136
144
  onProgress: () => { },
145
+ machine,
137
146
  });
138
147
  }
148
+ // The six seconds of sample narration back to back under a longer preview, the last cut to
149
+ // what is left.
150
+ function narrationFor(path, seconds) {
151
+ const pieces = [];
152
+ for (let at = 0; at < seconds - 0.001; at += sampleNarrationSeconds)
153
+ pieces.push({
154
+ kind: "body",
155
+ path,
156
+ seconds: Math.round(Math.min(sampleNarrationSeconds, seconds - at) * 1000) / 1000,
157
+ });
158
+ return pieces;
159
+ }
139
160
  function explained(log, error) {
140
161
  if (error instanceof StylePreviewError)
141
162
  return error;
142
- const message = error instanceof Error ? error.message : String(error);
163
+ const message = errorText(error);
143
164
  if (isMissingFont(error))
144
- return new StylePreviewError("font-missing", "The style preview couldn't render because the chosen caption font is no longer available; it may have been uninstalled or deleted. Choose another font under Subtitles, then press Render again.");
165
+ return new StylePreviewError("font-missing", "The style preview couldn't render because the chosen caption font is no longer available; it may have been uninstalled or deleted. Choose another caption font, then press Make the preview again.");
145
166
  if (message.startsWith("Slopify could not start ffmpeg") || /\bENOENT\b/.test(message))
146
- return new StylePreviewError("ffmpeg-missing", "The style preview couldn't render because ffmpeg wasn't found. Install ffmpeg, or use the Docker image, then press Render again.");
167
+ return new StylePreviewError("ffmpeg-missing", "The style preview couldn't render because ffmpeg wasn't found. Install ffmpeg, or use the Docker image, then press Make the preview again.");
147
168
  log.write("error", "style-preview.render", { detail: message });
148
- return new StylePreviewError("render-failed", "The style preview couldn't render because ffmpeg stopped with an error. Press Render again; if it fails again, use Download diagnostics in Settings and report it.");
169
+ return new StylePreviewError("render-failed", "The style preview couldn't render because ffmpeg stopped with an error. Press Make the preview again; if it fails again, use Download diagnostics in Settings and report it.");
149
170
  }
@@ -1,5 +1,6 @@
1
1
  import { z } from "zod";
2
2
  import { formats } from "../../kernel/pipeline.js";
3
+ import { motionStyles } from "../admission/model.js";
3
4
  import { videoEditSchema } from "../admission/schema.js";
4
5
  import { subtitleModes, subtitlePositions } from "../subtitles/model.js";
5
6
  // "See it before you make it": a few seconds rendered by the real renderer with a project's
@@ -8,6 +9,8 @@ import { subtitleModes, subtitlePositions } from "../subtitles/model.js";
8
9
  // How long the preview runs: three images of two seconds, so a transition and a chapter
9
10
  // card both show, under six seconds of the sample narration.
10
11
  export const stylePreviewSeconds = 6;
12
+ // The longest preview: the template studio's 30-second video sample.
13
+ export const stylePreviewSecondsMax = 30;
11
14
  export const stylePreviewTextMax = 200;
12
15
  // The sample text Play starts with. It, or no text, captions the sample narration with its own
13
16
  // words; any other text is spread over the stretches the narration speaks.
@@ -47,7 +50,17 @@ export const stylePreviewRequestSchema = z.object({
47
50
  title: z.string().max(shortTitlePreviewMax).optional(),
48
51
  })
49
52
  .optional(),
50
- // Render again even when this exact preview is already saved.
53
+ // How long it runs, in whole seconds; absent is six. The Shorts layout ignores it.
54
+ seconds: z.number().int().min(stylePreviewSeconds).max(stylePreviewSecondsMax).optional(),
55
+ // The run's own picture timing and motion; absent is two-second shots with the defaults.
56
+ timing: z
57
+ .object({
58
+ imageSeconds: z.number().finite().min(1).max(120),
59
+ zoomPercent: z.number().finite().min(0).max(100),
60
+ motionStyle: z.enum(motionStyles),
61
+ })
62
+ .optional(),
63
+ // Make the preview again even when this exact preview is already saved.
51
64
  force: z.boolean().optional(),
52
65
  });
53
66
  export const stylePreviewReplySchema = z.object({
@@ -1,6 +1,7 @@
1
1
  import { randomUUID } from "node:crypto";
2
2
  import { mkdirSync, readdirSync, renameSync, rmSync, statSync, writeFileSync } from "node:fs";
3
3
  import { join } from "node:path";
4
+ import { errorText } from "../../kernel/errors.js";
4
5
  import { normalizeStylePreview, previewSeconds, stylePreviewHash } from "./settings.js";
5
6
  // Rendered previews are kept in the data folder by the hash of their settings, so a setting
6
7
  // changed back, or the same style in another project, plays at once. `force` renders again
@@ -86,7 +87,7 @@ function prune(deps) {
86
87
  }
87
88
  catch (error) {
88
89
  deps.log.write("warn", "style-preview.prune", {
89
- detail: error instanceof Error ? error.message : String(error),
90
+ detail: errorText(error),
90
91
  });
91
92
  }
92
93
  }
@@ -61,8 +61,27 @@ image) {
61
61
  },
62
62
  chapterCard: edit.chapterCards ? { fontId: subtitles.fontId } : null,
63
63
  ...(image === undefined ? {} : { image }),
64
+ ...(request.seconds === undefined || request.seconds === stylePreviewSeconds
65
+ ? {}
66
+ : { seconds: request.seconds }),
67
+ ...(request.timing === undefined
68
+ ? {}
69
+ : {
70
+ timing: {
71
+ imageSeconds: Math.round(request.timing.imageSeconds * 10) / 10,
72
+ zoomPercent: Math.round(request.timing.zoomPercent * 10) / 10,
73
+ motionStyle: request.timing.motionStyle,
74
+ },
75
+ }),
64
76
  };
65
77
  }
78
+ // The shots a preview is cut into: two seconds each by default, else the run's own picture
79
+ // length, as many as fill its seconds (at least one).
80
+ export function previewShots(settings) {
81
+ const seconds = settings.seconds ?? stylePreviewSeconds;
82
+ const shotSeconds = settings.timing?.imageSeconds ?? stylePreviewSeconds / 3;
83
+ return { shotSeconds, shots: Math.max(1, Math.round(seconds / shotSeconds)) };
84
+ }
66
85
  // JSON with every object's keys in order, so equal settings always hash alike.
67
86
  export function stableJson(value) {
68
87
  if (Array.isArray(value))
@@ -80,9 +99,10 @@ export function stylePreviewHash(settings) {
80
99
  }
81
100
  // How long the rendered preview runs: a Shorts preview at a higher speed is shorter.
82
101
  export function previewSeconds(settings) {
83
- return settings.short === undefined
84
- ? stylePreviewSeconds
85
- : round(stylePreviewSeconds / settings.short.speed);
102
+ if (settings.short !== undefined)
103
+ return round(stylePreviewSeconds / settings.short.speed);
104
+ const { shotSeconds, shots } = previewShots(settings);
105
+ return round(shotSeconds * shots);
86
106
  }
87
107
  // ceiling: a pause this long between two words of the sample narration starts a new phrase,
88
108
  // the same pause `captionCues` starts a new cue at.
@@ -131,8 +151,16 @@ export function previewWords(text, narration = sampleNarrationWords) {
131
151
  }
132
152
  return timed;
133
153
  }
134
- export function previewCues(text) {
135
- return captionCues(previewWords(text));
154
+ // The captions of `seconds` of preview: the six seconds' words again every six seconds, as the
155
+ // sample narration repeats under a longer preview.
156
+ export function previewCues(text, seconds = stylePreviewSeconds) {
157
+ const once = previewWords(text);
158
+ const words = [];
159
+ for (let start = 0; start < seconds; start += stylePreviewSeconds)
160
+ for (const word of once)
161
+ if (start + word.end <= seconds)
162
+ words.push({ ...word, start: round(start + word.start), end: round(start + word.end) });
163
+ return captionCues(words);
136
164
  }
137
165
  function round(value) {
138
166
  return Math.round(value * 1000) / 1000;
@@ -24,7 +24,7 @@ export function captionCues(words) {
24
24
  !Number.isFinite(word.end) ||
25
25
  word.start < previousEnd - 0.001 ||
26
26
  word.end <= word.start) {
27
- throw new Error("The saved caption timing is damaged. Use More → Render the video again in the Video section to time them again, then Try again.");
27
+ throw new Error("The saved caption timing is damaged. Press Make the video again in the project's Video section to time them again, then Try again.");
28
28
  }
29
29
  const first = pending[0];
30
30
  if (first !== undefined &&
@@ -1,3 +1,4 @@
1
+ import { errorText } from "../../kernel/errors.js";
1
2
  // The collector URL is built into the release and is not user
2
3
  // configurable. The environment variable below is a test seam, not a documented knob:
3
4
  // packages/app/test/telemetry-flush.test.ts points it at a fake collector.
@@ -42,10 +43,7 @@ export function httpPostEvents(endpoint, timeoutMs) {
42
43
  catch (error) {
43
44
  // Offline is the ordinary case, not a fault: the events stay queued and the user is
44
45
  // told nothing.
45
- return { ok: false, retriable: true, reason: messageOf(error) };
46
+ return { ok: false, retriable: true, reason: errorText(error) };
46
47
  }
47
48
  };
48
49
  }
49
- function messageOf(error) {
50
- return error instanceof Error ? error.message : String(error);
51
- }
@@ -1,3 +1,4 @@
1
+ import { errorText } from "../../kernel/errors.js";
1
2
  import { machineOf, markDelivered, undeliveredEvents } from "./repo.js";
2
3
  // ceiling: 200 events per request against the collector's cap of 500, and at most 25
3
4
  // requests per flush. A queue longer than 5000 undelivered events drains over several
@@ -75,7 +76,7 @@ export function createFlusher(deps, delayMs) {
75
76
  .catch((error) => {
76
77
  // The timer has no caller to fail: a broken flush is logged and the next one
77
78
  // tries again.
78
- deps.log.write("warn", "telemetry.flush", { detail: messageOf(error) });
79
+ deps.log.write("warn", "telemetry.flush", { detail: errorText(error) });
79
80
  })
80
81
  .finally(() => {
81
82
  running = false;
@@ -106,6 +107,3 @@ function envelope(event, machineId) {
106
107
  createdAt: event.createdAt,
107
108
  };
108
109
  }
109
- function messageOf(error) {
110
- return error instanceof Error ? error.message : String(error);
111
- }
@@ -8,13 +8,6 @@ export const telemetryEventTypes = ["install", "project.created", "stage.complet
8
8
  // writes the intro and outro texts, the audio stage narrates all three - so the event type
9
9
  // stays `stage.completed` and the pair (stage, segment) says which unit it is.
10
10
  export const audioSegments = ["body", "intro", "outro"];
11
- export const noTokens = { tokensIn: 0, tokensOut: 0 };
12
- export function plusUsage(tokens, usage) {
13
- return {
14
- tokensIn: tokens.tokensIn + (usage?.inputTokens ?? 0),
15
- tokensOut: tokens.tokensOut + (usage?.outputTokens ?? 0),
16
- };
17
- }
18
11
  // The whole privacy surface. API keys, prompt bodies, keyword values, titles, article and
19
12
  // research text, files, filenames, OS, locale and hardware are all barred from a payload,
20
13
  // which is exactly what the first-run notice promises. The schema below is strict, so a
@@ -1,3 +1,4 @@
1
+ import { errorText } from "../../kernel/errors.js";
1
2
  import { payloadSchema } from "./model.js";
2
3
  import { insertTelemetryEvent, machineOf } from "./repo.js";
3
4
  // Appends one row to the local queue and hands it back. Throws: the notice dismissal
@@ -32,10 +33,16 @@ export function record(deps, type, counters) {
32
33
  }
33
34
  catch (error) {
34
35
  deps.log.write("warn", "telemetry.record", {
35
- detail: `${type}: ${messageOf(error)}`,
36
+ detail: `${type}: ${errorText(error)}`,
36
37
  });
37
38
  }
38
39
  }
39
- function messageOf(error) {
40
- return error instanceof Error ? error.message : String(error);
40
+ // A template studio sample (or bake-off copy) is the studio's, not a video of the person's:
41
+ // Settings → Usage and the public counters leave its stages out.
42
+ export function skippingProjects(count, skipped) {
43
+ return (type, counters, projectId) => {
44
+ if (projectId !== undefined && skipped(projectId))
45
+ return;
46
+ count(type, counters, projectId);
47
+ };
41
48
  }