@gentbajko/slopify 2.5.0 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (446) hide show
  1. package/LICENSE +201 -21
  2. package/NOTICE +12 -0
  3. package/README.md +21 -40
  4. package/SUBTITLES.md +1 -1
  5. package/dist/adapter-registry.js +3 -0
  6. package/dist/adapters/alignment/cache.js +4 -1
  7. package/dist/adapters/alignment/ctc.js +25 -23
  8. package/dist/adapters/alignment/index.js +37 -6
  9. package/dist/adapters/alignment/multilingual.js +267 -0
  10. package/dist/adapters/alignment/protocol.js +11 -0
  11. package/dist/adapters/alignment/quality.js +4 -3
  12. package/dist/adapters/alignment/sentences.js +95 -0
  13. package/dist/adapters/alignment/spec.js +24 -0
  14. package/dist/adapters/alignment/spell.js +769 -0
  15. package/dist/adapters/alignment/text.js +56 -9
  16. package/dist/adapters/alignment/window.js +34 -30
  17. package/dist/adapters/alignment/worker.js +23 -21
  18. package/dist/adapters/explain.js +11 -11
  19. package/dist/adapters/host-cli/index.js +44 -5
  20. package/dist/adapters/host-cli/transport.js +7 -5
  21. package/dist/adapters/image/bytes.js +2 -2
  22. package/dist/adapters/image/codex-output.js +1 -1
  23. package/dist/adapters/image/codex.js +87 -22
  24. package/dist/adapters/image/fal.js +13 -5
  25. package/dist/adapters/image/google.js +11 -8
  26. package/dist/adapters/image/openai.js +14 -7
  27. package/dist/adapters/image/reference.js +34 -0
  28. package/dist/adapters/image/replicate.js +7 -2
  29. package/dist/adapters/image/video.js +2 -2
  30. package/dist/adapters/key-probes.js +36 -0
  31. package/dist/adapters/llm/claude-code.js +168 -32
  32. package/dist/adapters/llm/cli-login-error.js +1 -1
  33. package/dist/adapters/llm/codex-limits.js +157 -0
  34. package/dist/adapters/llm/codex.js +99 -12
  35. package/dist/adapters/llm/document-workspace.js +2 -2
  36. package/dist/adapters/llm/gemini-workspace.js +1 -1
  37. package/dist/adapters/llm/gemini.js +16 -3
  38. package/dist/adapters/llm/image-workspace.js +58 -0
  39. package/dist/adapters/llm/openrouter.js +11 -3
  40. package/dist/adapters/llm/run-cli.js +12 -7
  41. package/dist/adapters/tts/cartesia.js +10 -0
  42. package/dist/adapters/tts/elevenlabs.js +31 -9
  43. package/dist/adapters/tts/inworld-async.js +9 -5
  44. package/dist/adapters/tts/inworld.js +17 -3
  45. package/dist/adapters/tts/openai.js +3 -0
  46. package/dist/adapters/tts/voice-languages.js +80 -0
  47. package/dist/assets/fonts/noto/NotoSans-Regular.ttf +0 -0
  48. package/dist/assets/fonts/noto/NotoSansArabic-Regular.ttf +0 -0
  49. package/dist/assets/fonts/noto/NotoSansDevanagari-Regular.ttf +0 -0
  50. package/dist/assets/fonts/noto/NotoSansHebrew-Regular.ttf +0 -0
  51. package/dist/assets/fonts/noto/NotoSansThai-Regular.ttf +0 -0
  52. package/dist/assets/fonts/noto/OFL.txt +93 -0
  53. package/dist/assets/fonts/noto/SOURCE.txt +18 -0
  54. package/dist/assets/sample/sample-audiobook.tar +0 -0
  55. package/dist/assets/sample/sample-podcast.tar +0 -0
  56. package/dist/assets/sample/sample-project.tar +0 -0
  57. package/dist/catalog/merge.js +132 -0
  58. package/dist/catalog/registry.js +13 -13
  59. package/dist/catalog/schema.js +2 -0
  60. package/dist/catalog/store.js +147 -8
  61. package/dist/compose.yaml +71 -0
  62. package/dist/edge/autostart/docker-record.js +32 -0
  63. package/dist/edge/autostart/docker.js +50 -0
  64. package/dist/edge/autostart/index.js +78 -0
  65. package/dist/edge/autostart/launcher.js +176 -0
  66. package/dist/edge/autostart/model.js +3 -0
  67. package/dist/edge/autostart/native.js +210 -0
  68. package/dist/edge/autostart/prompt.js +66 -0
  69. package/dist/edge/autostart/service.js +81 -0
  70. package/dist/edge/cli.js +57 -51
  71. package/dist/edge/docker-install/activation.js +56 -0
  72. package/dist/edge/docker-install/apply.js +458 -0
  73. package/dist/edge/docker-install/engine.js +362 -0
  74. package/dist/edge/docker-install/run.js +154 -0
  75. package/dist/edge/docker-install/state.js +146 -0
  76. package/dist/edge/{docker-projects → docker-install}/volume.js +1 -5
  77. package/dist/edge/docker.js +14 -22
  78. package/dist/edge/host-cli.js +1 -1
  79. package/dist/edge/http/actions.js +32 -10
  80. package/dist/edge/http/app.js +58 -2
  81. package/dist/edge/http/auditions.js +70 -0
  82. package/dist/edge/http/autostart.js +37 -0
  83. package/dist/edge/http/channel-memory.js +92 -0
  84. package/dist/edge/http/channels.js +128 -0
  85. package/dist/edge/http/entries.js +22 -3
  86. package/dist/edge/http/files.js +1 -1
  87. package/dist/edge/http/folder-location.js +1 -1
  88. package/dist/edge/http/home.js +39 -0
  89. package/dist/edge/http/host-cli.js +29 -5
  90. package/dist/edge/http/narration-peaks.js +76 -0
  91. package/dist/edge/http/onboarding.js +152 -0
  92. package/dist/edge/http/open-folder.js +1 -1
  93. package/dist/edge/http/patch-notes.js +45 -0
  94. package/dist/edge/http/planning.js +4 -2
  95. package/dist/edge/http/project-create.js +87 -0
  96. package/dist/edge/http/projects.js +31 -99
  97. package/dist/edge/http/prompts.js +40 -3
  98. package/dist/edge/http/pronunciations.js +23 -4
  99. package/dist/edge/http/providers.js +86 -0
  100. package/dist/edge/http/reviews.js +121 -0
  101. package/dist/edge/http/revision-files.js +1 -1
  102. package/dist/edge/http/revisions.js +1 -1
  103. package/dist/edge/http/run-cost.js +25 -0
  104. package/dist/edge/http/schedules.js +111 -6
  105. package/dist/edge/http/settings.js +84 -6
  106. package/dist/edge/http/storage-files.js +50 -0
  107. package/dist/edge/http/storage.js +29 -2
  108. package/dist/edge/http/studio.js +207 -0
  109. package/dist/edge/http/style-preview.js +100 -0
  110. package/dist/edge/http/subtitles.js +1 -1
  111. package/dist/edge/http/trash.js +69 -0
  112. package/dist/edge/http/update.js +24 -3
  113. package/dist/edge/http/whats-new.js +15 -0
  114. package/dist/edge/http/youtube-edits.js +83 -0
  115. package/dist/edge/native-update.js +62 -0
  116. package/dist/host-cli/open-folder.js +10 -10
  117. package/dist/host-cli/runtime.js +9 -1
  118. package/dist/kernel/db/migrations/0025-automatic-reviews.sql +40 -0
  119. package/dist/kernel/db/migrations/0026-schedule-topic-generation.sql +23 -0
  120. package/dist/kernel/db/migrations/0027-run-cost.sql +58 -0
  121. package/dist/kernel/db/migrations/0030-automatic-retries.sql +18 -0
  122. package/dist/kernel/db/migrations/0031-prompt-history-and-description-edits.sql +33 -0
  123. package/dist/kernel/db/migrations/0032-channels.sql +65 -0
  124. package/dist/kernel/db/migrations/0034-script-prompts.sql +23 -0
  125. package/dist/kernel/db/migrations/0035-narration-aliases.sql +13 -0
  126. package/dist/kernel/db/migrations/0037-project-uploads.sql +8 -0
  127. package/dist/kernel/db/migrations/0038-voice-languages.sql +7 -0
  128. package/dist/kernel/db/migrations/0039-channel-essentials.sql +64 -0
  129. package/dist/kernel/db/migrations/0040-standalone-usage.sql +35 -0
  130. package/dist/kernel/paths.js +27 -6
  131. package/dist/kernel/pipeline.js +4 -1
  132. package/dist/kernel/ports/host-cli.js +122 -14
  133. package/dist/kernel/ports/key-probe.js +1 -0
  134. package/dist/kernel/ports/languages.js +157 -0
  135. package/dist/kernel/ports/model.js +5 -2
  136. package/dist/kernel/ports/narration-aliases.js +60 -0
  137. package/dist/kernel/ports/plan-limits.js +36 -0
  138. package/dist/kernel/runner/attempt.js +17 -8
  139. package/dist/kernel/runner/audition.js +36 -0
  140. package/dist/kernel/runner/graph.js +24 -5
  141. package/dist/kernel/runner/index.js +35 -5
  142. package/dist/kernel/runner/meter.js +1 -0
  143. package/dist/kernel/runner/providers.js +142 -7
  144. package/dist/kernel/runner/retry-policy.js +43 -0
  145. package/dist/kernel/runner/standalone.js +134 -0
  146. package/dist/kernel/runner/work-authority.js +28 -2
  147. package/dist/main.js +272 -16
  148. package/dist/patch-notes/0.4.2-to-1.0.md +93 -0
  149. package/dist/patch-notes/1.0-to-2.0.md +74 -0
  150. package/dist/patch-notes/2.0-to-3.0.md +257 -0
  151. package/dist/patch-notes/3.0.0.md +70 -0
  152. package/dist/patch-notes/index.json +26 -0
  153. package/dist/slices/admission/model.js +27 -0
  154. package/dist/slices/admission/repo.js +24 -4
  155. package/dist/slices/admission/rules.js +79 -1
  156. package/dist/slices/admission/schema.js +58 -1
  157. package/dist/slices/admission/short-mode.js +47 -0
  158. package/dist/slices/admission/start.js +41 -5
  159. package/dist/slices/article/continuation.js +2 -2
  160. package/dist/slices/article/provided-entries.js +2 -2
  161. package/dist/slices/article/run.js +6 -2
  162. package/dist/slices/article/segments.js +3 -3
  163. package/dist/slices/backups/folder.js +14 -6
  164. package/dist/slices/batch/index.js +7 -2
  165. package/dist/slices/cancel/index.js +1 -1
  166. package/dist/slices/channels/cast-images.js +110 -0
  167. package/dist/slices/channels/cast-match.js +32 -0
  168. package/dist/slices/channels/images.js +19 -0
  169. package/dist/slices/channels/model.js +5 -0
  170. package/dist/slices/channels/rebrand.js +100 -0
  171. package/dist/slices/channels/repo.js +218 -0
  172. package/dist/slices/channels/runs.js +129 -0
  173. package/dist/slices/channels/schema.js +129 -0
  174. package/dist/slices/channels/service.js +180 -0
  175. package/dist/slices/channels/studio-csv.js +110 -0
  176. package/dist/slices/channels/videos.js +120 -0
  177. package/dist/slices/checkpoints/recovery.js +10 -0
  178. package/dist/slices/control/index.js +2 -1
  179. package/dist/slices/document/fonts.js +1 -1
  180. package/dist/slices/document/render.js +1 -1
  181. package/dist/slices/episodes/related.js +90 -0
  182. package/dist/slices/episodes/repo.js +60 -0
  183. package/dist/slices/episodes/service.js +58 -0
  184. package/dist/slices/episodes/summarize.js +170 -0
  185. package/dist/slices/estimate/index.js +109 -28
  186. package/dist/slices/estimate/requests.js +60 -2
  187. package/dist/slices/fixes/rules.js +73 -0
  188. package/dist/slices/fonts/coverage.js +82 -0
  189. package/dist/slices/fonts/sfnt.js +74 -0
  190. package/dist/slices/images/run.js +8 -5
  191. package/dist/slices/images/scale.js +84 -0
  192. package/dist/slices/library/diff.js +122 -0
  193. package/dist/slices/library/history.js +100 -0
  194. package/dist/slices/library/model.js +3 -0
  195. package/dist/slices/library/photorealistic.js +49 -0
  196. package/dist/slices/library/repo.js +19 -14
  197. package/dist/slices/library/save.js +62 -12
  198. package/dist/slices/library/slots.js +7 -1
  199. package/dist/slices/library/used-by.js +153 -0
  200. package/dist/slices/loudness/level-pieces.js +41 -0
  201. package/dist/slices/loudness/loudnorm.js +164 -0
  202. package/dist/slices/loudness/model.js +153 -0
  203. package/dist/slices/model-upkeep/model.js +10 -0
  204. package/dist/slices/model-upkeep/switch.js +217 -0
  205. package/dist/slices/model-upkeep/usage.js +183 -0
  206. package/dist/slices/narration/aliases-library.js +31 -0
  207. package/dist/slices/narration/aliases-schema.js +53 -0
  208. package/dist/slices/narration/aliases.js +49 -0
  209. package/dist/slices/narration/blocks.js +352 -0
  210. package/dist/slices/narration/chunk.js +19 -7
  211. package/dist/slices/narration/concat.js +2 -2
  212. package/dist/slices/narration/describe.js +45 -0
  213. package/dist/slices/narration/pauses-model.js +63 -0
  214. package/dist/slices/narration/pauses.js +269 -0
  215. package/dist/slices/narration/peaks-model.js +1 -0
  216. package/dist/slices/narration/peaks.js +94 -0
  217. package/dist/slices/narration/preparation.js +21 -3
  218. package/dist/slices/narration/pronunciation-chunks.js +3 -3
  219. package/dist/slices/narration/pronunciation.js +72 -25
  220. package/dist/slices/narration/run.js +6 -6
  221. package/dist/slices/narration/shared-glossary.js +1 -1
  222. package/dist/slices/narration/steering.js +15 -4
  223. package/dist/slices/notifications/notifier.js +21 -3
  224. package/dist/slices/notifications/rules.js +22 -1
  225. package/dist/slices/onboarding/copy.js +143 -0
  226. package/dist/slices/onboarding/first-run.js +46 -0
  227. package/dist/slices/onboarding/install.js +119 -0
  228. package/dist/slices/onboarding/model.js +64 -0
  229. package/dist/slices/onboarding/packs.js +491 -0
  230. package/dist/slices/onboarding/quick-short.js +129 -0
  231. package/dist/slices/onboarding/sample.js +132 -0
  232. package/dist/slices/onboarding/state.js +68 -0
  233. package/dist/slices/onboarding/template.js +73 -0
  234. package/dist/slices/patch-notes/library.js +73 -0
  235. package/dist/slices/patch-notes/seen.js +77 -0
  236. package/dist/slices/play-drafts/convert.js +125 -6
  237. package/dist/slices/play-drafts/readiness.js +3 -0
  238. package/dist/slices/play-drafts/repo.js +2 -0
  239. package/dist/slices/play-drafts/review-inputs.js +20 -3
  240. package/dist/slices/play-drafts/schema.js +89 -0
  241. package/dist/slices/play-drafts/service.js +4 -1
  242. package/dist/slices/play-drafts/start.js +43 -18
  243. package/dist/slices/project-templates/from-project.js +50 -4
  244. package/dist/slices/project-templates/one-off.js +26 -0
  245. package/dist/slices/project-templates/repo.js +2 -1
  246. package/dist/slices/project-templates/service.js +15 -4
  247. package/dist/slices/project-templates/setup.js +18 -2
  248. package/dist/slices/rebuild/admission-repo.js +1 -1
  249. package/dist/slices/rebuild/model.js +6 -0
  250. package/dist/slices/rebuild/preview-details.js +46 -3
  251. package/dist/slices/rebuild/preview-plan.js +13 -6
  252. package/dist/slices/rebuild/recipe-audio-parts.js +40 -7
  253. package/dist/slices/rebuild/recipe-audio.js +66 -12
  254. package/dist/slices/rebuild/recipe-build.js +8 -4
  255. package/dist/slices/rebuild/recipe-cast.js +18 -0
  256. package/dist/slices/rebuild/recipe-checkpoints.js +11 -9
  257. package/dist/slices/rebuild/recipe-describe.js +93 -0
  258. package/dist/slices/rebuild/recipe-edit.js +13 -1
  259. package/dist/slices/rebuild/recipe-exports.js +62 -8
  260. package/dist/slices/rebuild/recipe-input-schema.js +35 -0
  261. package/dist/slices/rebuild/recipe-legacy.js +9 -0
  262. package/dist/slices/rebuild/recipe-loudness.js +45 -0
  263. package/dist/slices/rebuild/recipe-model.js +25 -6
  264. package/dist/slices/rebuild/recipe-pauses.js +7 -0
  265. package/dist/slices/rebuild/recipe-preparation.js +3 -3
  266. package/dist/slices/rebuild/recipe-provider-choice.js +11 -1
  267. package/dist/slices/rebuild/recipe-reference.js +15 -1
  268. package/dist/slices/rebuild/recipe-reviews.js +117 -0
  269. package/dist/slices/rebuild/recipe-save.js +4 -1
  270. package/dist/slices/rebuild/recipe-shorts.js +23 -3
  271. package/dist/slices/rebuild/recipe-text.js +114 -19
  272. package/dist/slices/rebuild/recipe-validation.js +14 -2
  273. package/dist/slices/rebuild/recipe-visual.js +96 -33
  274. package/dist/slices/rebuild/recipe-voices.js +286 -0
  275. package/dist/slices/rebuild/recipe-work.js +77 -6
  276. package/dist/slices/rebuild/recipe-youtube.js +2 -0
  277. package/dist/slices/rebuild/recovery-conflict.js +7 -3
  278. package/dist/slices/rebuild/recovery-model.js +3 -0
  279. package/dist/slices/rebuild/recovery-selection.js +28 -2
  280. package/dist/slices/rebuild/recovery.js +35 -17
  281. package/dist/slices/rebuild/repo.js +3 -3
  282. package/dist/slices/rebuild/retry.js +18 -0
  283. package/dist/slices/rebuild/review-redo.js +110 -0
  284. package/dist/slices/rebuild/runtime-actions.js +5 -0
  285. package/dist/slices/rebuild/runtime-admission.js +4 -1
  286. package/dist/slices/rebuild/runtime-animate.js +4 -4
  287. package/dist/slices/rebuild/runtime-article.js +5 -5
  288. package/dist/slices/rebuild/runtime-document.js +4 -4
  289. package/dist/slices/rebuild/runtime-export-bed.js +43 -0
  290. package/dist/slices/rebuild/runtime-export-edit.js +70 -11
  291. package/dist/slices/rebuild/runtime-export-inputs.js +52 -10
  292. package/dist/slices/rebuild/runtime-export-short.js +85 -0
  293. package/dist/slices/rebuild/runtime-export.js +84 -19
  294. package/dist/slices/rebuild/runtime-figure-card.js +87 -0
  295. package/dist/slices/rebuild/runtime-image.js +21 -2
  296. package/dist/slices/rebuild/runtime-local.js +148 -24
  297. package/dist/slices/rebuild/runtime-materialize.js +8 -0
  298. package/dist/slices/rebuild/runtime-narration-reuse.js +2 -1
  299. package/dist/slices/rebuild/runtime-narration-text.js +7 -7
  300. package/dist/slices/rebuild/runtime-pictures.js +28 -0
  301. package/dist/slices/rebuild/runtime-piece-label.js +27 -0
  302. package/dist/slices/rebuild/runtime-provider.js +126 -18
  303. package/dist/slices/rebuild/runtime-publication.js +21 -5
  304. package/dist/slices/rebuild/runtime-review.js +237 -0
  305. package/dist/slices/rebuild/runtime-run.js +17 -11
  306. package/dist/slices/rebuild/runtime-shorts.js +44 -17
  307. package/dist/slices/rebuild/runtime-store.js +39 -10
  308. package/dist/slices/rebuild/runtime-subtitles.js +72 -19
  309. package/dist/slices/rebuild/runtime-voices.js +103 -0
  310. package/dist/slices/rebuild/runtime-youtube.js +7 -6
  311. package/dist/slices/rebuild/soften.js +49 -0
  312. package/dist/slices/rebuild/transition-repo.js +9 -2
  313. package/dist/slices/research/run.js +4 -4
  314. package/dist/slices/research/synthesis.js +2 -2
  315. package/dist/slices/reviews/model.js +105 -0
  316. package/dist/slices/reviews/outcome.js +22 -0
  317. package/dist/slices/reviews/repo.js +106 -0
  318. package/dist/slices/reviews/rules.js +63 -0
  319. package/dist/slices/reviews/verdict.js +71 -0
  320. package/dist/slices/reviews/view.js +30 -0
  321. package/dist/slices/revisions/adopt-content.js +5 -2
  322. package/dist/slices/revisions/adopt.js +3 -1
  323. package/dist/slices/revisions/downloads.js +7 -1
  324. package/dist/slices/revisions/image-plan.js +183 -0
  325. package/dist/slices/revisions/mutation-assets.js +10 -0
  326. package/dist/slices/revisions/mutation-cues.js +71 -0
  327. package/dist/slices/revisions/mutation-request.js +2 -1
  328. package/dist/slices/revisions/mutations.js +97 -3
  329. package/dist/slices/revisions/schema.js +11 -1
  330. package/dist/slices/run-cost/limits.js +128 -0
  331. package/dist/slices/run-cost/meter.js +47 -0
  332. package/dist/slices/run-cost/panel.js +234 -0
  333. package/dist/slices/run-cost/pricing.js +92 -0
  334. package/dist/slices/run-cost/week.js +108 -0
  335. package/dist/slices/schedules/agenda.js +158 -0
  336. package/dist/slices/schedules/repo.js +58 -18
  337. package/dist/slices/schedules/scheduler.js +67 -16
  338. package/dist/slices/schedules/schema.js +138 -1
  339. package/dist/slices/schedules/service.js +43 -4
  340. package/dist/slices/schedules/similar.js +90 -0
  341. package/dist/slices/schedules/topic-list.js +163 -0
  342. package/dist/slices/schedules/topics.js +422 -0
  343. package/dist/slices/settings/first-run.js +102 -0
  344. package/dist/slices/settings/health.js +190 -0
  345. package/dist/slices/settings/key-guides.js +128 -0
  346. package/dist/slices/settings/key-test.js +85 -0
  347. package/dist/slices/settings/model.js +6 -1
  348. package/dist/slices/settings/playback.js +15 -0
  349. package/dist/slices/settings/repo.js +17 -3
  350. package/dist/slices/settings/voices.js +64 -4
  351. package/dist/slices/settings/whats-new.js +64 -0
  352. package/dist/slices/shorts/captions.js +9 -0
  353. package/dist/slices/shorts/pick.js +3 -3
  354. package/dist/slices/shorts/prompts.js +1 -1
  355. package/dist/slices/shorts/render.js +54 -5
  356. package/dist/slices/storage/asset-name.js +3 -0
  357. package/dist/slices/storage/backup-export.js +48 -9
  358. package/dist/slices/storage/backup-format.js +38 -1
  359. package/dist/slices/storage/backup-import.js +191 -19
  360. package/dist/slices/storage/delete-project.js +7 -4
  361. package/dist/slices/storage/documents.js +79 -0
  362. package/dist/slices/storage/files-location.js +531 -0
  363. package/dist/slices/storage/layout.js +14 -4
  364. package/dist/slices/storage/model.js +11 -0
  365. package/dist/slices/storage/portable.js +92 -12
  366. package/dist/slices/storage/reconcile.js +14 -1
  367. package/dist/slices/storage/repo.js +4 -1
  368. package/dist/slices/storage/schema.js +11 -0
  369. package/dist/slices/storage/trim.js +146 -0
  370. package/dist/slices/studio/disclosure.js +74 -0
  371. package/dist/slices/studio/model.js +28 -0
  372. package/dist/slices/studio/pack.js +209 -0
  373. package/dist/slices/studio/settings.js +133 -0
  374. package/dist/slices/style-preview/images.js +61 -0
  375. package/dist/slices/style-preview/render.js +161 -0
  376. package/dist/slices/style-preview/schema.js +45 -0
  377. package/dist/slices/style-preview/service.js +93 -0
  378. package/dist/slices/style-preview/settings.js +89 -0
  379. package/dist/slices/subtitles/captions.js +42 -9
  380. package/dist/slices/subtitles/model.js +22 -0
  381. package/dist/slices/subtitles/prepare.js +27 -16
  382. package/dist/slices/subtitles/transcript.js +1 -1
  383. package/dist/slices/thumbnail/run.js +7 -7
  384. package/dist/slices/trash/model.js +25 -0
  385. package/dist/slices/trash/service.js +273 -0
  386. package/dist/slices/uploads/repo.js +20 -0
  387. package/dist/slices/video/ambient-bed-schema.js +15 -0
  388. package/dist/slices/video/ambient-bed.js +95 -0
  389. package/dist/slices/video/ambient-mix.js +72 -0
  390. package/dist/slices/video/audio-inputs.js +2 -2
  391. package/dist/slices/video/cards.js +5 -2
  392. package/dist/slices/video/edit-list.js +23 -2
  393. package/dist/slices/video/edit-settings.js +13 -3
  394. package/dist/slices/video/ffmpeg.js +63 -9
  395. package/dist/slices/video/figure-card.js +492 -0
  396. package/dist/slices/video/figure-spans.js +51 -0
  397. package/dist/slices/video/plan.js +125 -13
  398. package/dist/slices/video/run.js +14 -12
  399. package/dist/slices/video/slideshow.js +39 -4
  400. package/dist/slices/voices/attribution.js +69 -0
  401. package/dist/slices/voices/audio-files.js +82 -0
  402. package/dist/slices/voices/audition.js +25 -0
  403. package/dist/slices/voices/cast.js +56 -0
  404. package/dist/slices/voices/grouping.js +49 -0
  405. package/dist/slices/voices/join.js +35 -0
  406. package/dist/slices/voices/languages.js +25 -0
  407. package/dist/slices/voices/model.js +179 -0
  408. package/dist/slices/voices/palette.js +26 -0
  409. package/dist/slices/voices/panel.js +91 -0
  410. package/dist/slices/voices/portraits.js +55 -0
  411. package/dist/slices/voices/script.js +136 -0
  412. package/dist/slices/voices/timing.js +58 -0
  413. package/dist/slices/youtube/answer.js +1 -1
  414. package/dist/slices/youtube/chapters.js +126 -0
  415. package/dist/slices/youtube/edits-repo.js +69 -0
  416. package/dist/slices/youtube/edits.js +89 -0
  417. package/dist/slices/youtube/placeholders.js +98 -0
  418. package/dist/slices/youtube/transcript.js +11 -4
  419. package/dist/updater/service.js +66 -21
  420. package/dist/web/assets/index-CUA2sUxU.css +1 -0
  421. package/dist/web/assets/index-CX590kE-.js +303 -0
  422. package/dist/web/assets/jetbrains-mono-cyrillic-400-normal-BEIGL1Tu.woff2 +0 -0
  423. package/dist/web/assets/jetbrains-mono-cyrillic-400-normal-ugxPyKxw.woff +0 -0
  424. package/dist/web/assets/jetbrains-mono-greek-400-normal-B9oWc5Lo.woff +0 -0
  425. package/dist/web/assets/jetbrains-mono-greek-400-normal-C190GLew.woff2 +0 -0
  426. package/dist/web/assets/jetbrains-mono-latin-400-normal-6-qcROiO.woff +0 -0
  427. package/dist/web/assets/jetbrains-mono-latin-400-normal-V6pRDFza.woff2 +0 -0
  428. package/dist/web/assets/jetbrains-mono-latin-ext-400-normal-Bc8Ftmh3.woff2 +0 -0
  429. package/dist/web/assets/jetbrains-mono-latin-ext-400-normal-fXTG6kC5.woff +0 -0
  430. package/dist/web/assets/jetbrains-mono-vietnamese-400-normal-CqNFfHCs.woff +0 -0
  431. package/dist/web/assets/{pdf-BLF2i8c2.js → pdf-BG8n_uc5.js} +1 -1
  432. package/dist/web/index.html +2 -2
  433. package/package.json +4 -4
  434. package/dist/edge/docker-launch.js +0 -81
  435. package/dist/edge/docker-projects/activation.js +0 -35
  436. package/dist/edge/docker-projects/claims.js +0 -85
  437. package/dist/edge/docker-projects/committed.js +0 -40
  438. package/dist/edge/docker-projects/engine.js +0 -461
  439. package/dist/edge/docker-projects/install.js +0 -254
  440. package/dist/edge/docker-projects/prune.js +0 -66
  441. package/dist/edge/docker-projects/recover.js +0 -100
  442. package/dist/edge/docker-projects/state.js +0 -225
  443. package/dist/web/assets/index-CktpxlJd.js +0 -171
  444. package/dist/web/assets/index-DB-vOnGU.css +0 -1
  445. package/scripts/docker-run.sh +0 -3
  446. /package/dist/edge/{docker-projects → docker-install}/tree.js +0 -0
@@ -0,0 +1,157 @@
1
+ import { z } from "zod";
2
+ // The language a project is made in: what the LLM writes, what the voice speaks, how the
3
+ // narration is split into sentences and how the captions are timed. Absent on every run
4
+ // config, draft, template and channel saved before it existed, and absent is English, so
5
+ // nothing about those changes. Only a language other than English is ever stored.
6
+ export const writingSystems = [
7
+ "latin",
8
+ "cyrillic",
9
+ "greek",
10
+ "arabic",
11
+ "hebrew",
12
+ "devanagari",
13
+ "thai",
14
+ "cjk",
15
+ "hangul",
16
+ ];
17
+ export const languageList = [
18
+ { code: "en", name: "English", native: "English", script: "latin", timing: "english" },
19
+ { code: "es", name: "Spanish", native: "Español", script: "latin", timing: "multilingual" },
20
+ { code: "de", name: "German", native: "Deutsch", script: "latin", timing: "multilingual" },
21
+ { code: "fr", name: "French", native: "Français", script: "latin", timing: "multilingual" },
22
+ { code: "it", name: "Italian", native: "Italiano", script: "latin", timing: "multilingual" },
23
+ { code: "pt", name: "Portuguese", native: "Português", script: "latin", timing: "multilingual" },
24
+ { code: "nl", name: "Dutch", native: "Nederlands", script: "latin", timing: "multilingual" },
25
+ { code: "ca", name: "Catalan", native: "Català", script: "latin", timing: "multilingual" },
26
+ { code: "pl", name: "Polish", native: "Polski", script: "latin", timing: "multilingual" },
27
+ { code: "cs", name: "Czech", native: "Čeština", script: "latin", timing: "multilingual" },
28
+ { code: "ro", name: "Romanian", native: "Română", script: "latin", timing: "sentences" },
29
+ { code: "sv", name: "Swedish", native: "Svenska", script: "latin", timing: "sentences" },
30
+ { code: "da", name: "Danish", native: "Dansk", script: "latin", timing: "sentences" },
31
+ { code: "nb", name: "Norwegian", native: "Norsk bokmål", script: "latin", timing: "sentences" },
32
+ { code: "fi", name: "Finnish", native: "Suomi", script: "latin", timing: "sentences" },
33
+ { code: "hu", name: "Hungarian", native: "Magyar", script: "latin", timing: "sentences" },
34
+ { code: "tr", name: "Turkish", native: "Türkçe", script: "latin", timing: "sentences" },
35
+ {
36
+ code: "id",
37
+ name: "Indonesian",
38
+ native: "Bahasa Indonesia",
39
+ script: "latin",
40
+ timing: "sentences",
41
+ },
42
+ { code: "vi", name: "Vietnamese", native: "Tiếng Việt", script: "latin", timing: "sentences" },
43
+ { code: "el", name: "Greek", native: "Ελληνικά", script: "greek", timing: "sentences" },
44
+ { code: "ru", name: "Russian", native: "Русский", script: "cyrillic", timing: "sentences" },
45
+ { code: "uk", name: "Ukrainian", native: "Українська", script: "cyrillic", timing: "sentences" },
46
+ { code: "ar", name: "Arabic", native: "العربية", script: "arabic", timing: "sentences" },
47
+ { code: "he", name: "Hebrew", native: "עברית", script: "hebrew", timing: "sentences" },
48
+ { code: "hi", name: "Hindi", native: "हिन्दी", script: "devanagari", timing: "sentences" },
49
+ { code: "th", name: "Thai", native: "ไทย", script: "thai", timing: "sentences" },
50
+ { code: "ja", name: "Japanese", native: "日本語", script: "cjk", timing: "sentences" },
51
+ { code: "zh", name: "Chinese", native: "中文", script: "cjk", timing: "sentences" },
52
+ { code: "ko", name: "Korean", native: "한국어", script: "hangul", timing: "sentences" },
53
+ ];
54
+ export const languageCodes = languageList.map((language) => language.code);
55
+ export const defaultLanguage = "en";
56
+ export const languageSchema = z.enum(languageCodes);
57
+ export function languageInfo(code) {
58
+ return (languageList.find((language) => language.code === (code ?? defaultLanguage)) ?? languageList[0]);
59
+ }
60
+ // The project's language; absent is English.
61
+ export function projectLanguage(config) {
62
+ return config.language ?? defaultLanguage;
63
+ }
64
+ export function isEnglish(config) {
65
+ return projectLanguage(config) === defaultLanguage;
66
+ }
67
+ // The line added to every prompt that writes for the audience, when the project is not in
68
+ // English. It is the app's own sentence, added after the user's prompt rather than written
69
+ // into it, so the prompt library stays as the user wrote it; an English project gets nothing,
70
+ // and its requests (and their fingerprints) stay exactly as they were.
71
+ export function languageInstruction(language) {
72
+ if (language === undefined || language === defaultLanguage)
73
+ return undefined;
74
+ const { name, native } = languageInfo(language);
75
+ return `Language: write everything meant for the audience (article, narration, titles, descriptions, chapter names, hashtags and tags) in ${name} (${native}), even where the instructions above are written in English. Keep JSON keys, markup and any heading or label the instructions say to use exactly as given.`;
76
+ }
77
+ // The messages with the language line added: to the system message when there is one, since
78
+ // the last user message is sometimes the source text itself; otherwise to the end of the last
79
+ // user message. English returns the very same messages.
80
+ export function withLanguage(messages, language) {
81
+ const instruction = languageInstruction(language);
82
+ if (instruction === undefined)
83
+ return messages;
84
+ const system = messages.findIndex((message) => message.role === "system");
85
+ const target = system !== -1 ? system : messages.findLastIndex((message) => message.role === "user");
86
+ if (target === -1)
87
+ return messages;
88
+ return messages.map((message, index) => index === target ? { ...message, content: `${message.content}\n\n${instruction}` } : message);
89
+ }
90
+ // Why word-by-word features are off in a language timed by sentences, or undefined when they
91
+ // are available. The same sentence is shown beside every control it turns off.
92
+ export function wordTimingUnavailable(code) {
93
+ const language = languageInfo(code);
94
+ if (language.timing !== "sentences")
95
+ return undefined;
96
+ return `Word timing isn't available for ${language.name} yet, so captions are timed sentence by sentence: each sentence is shown for the part of the narration that reads it. Word-by-word captions and cuts that follow the narration need word timing and are off.`;
97
+ }
98
+ // The letters a caption font needs for a language: its alphabet beyond a–z, or a sample of
99
+ // its script. A font missing any of them draws empty boxes in that language's captions.
100
+ const extraLetters = {
101
+ es: "áéíóúüñ¿¡",
102
+ de: "äöüß",
103
+ fr: "àâæçèéêëîïôœùûüÿ",
104
+ it: "àèéìíîòóù",
105
+ pt: "áâãàçéêíóôõú",
106
+ ca: "àèéíïòóúüç·",
107
+ nl: "éëïóöü",
108
+ pl: "ąćęłńóśźż",
109
+ cs: "áčďéěíňóřšťúůýž",
110
+ ro: "ăâîșț",
111
+ sv: "åäö",
112
+ da: "æøå",
113
+ nb: "æøå",
114
+ fi: "äö",
115
+ hu: "áéíóöőúüű",
116
+ tr: "çğıöşü",
117
+ vi: "ăâđêôơưạảấầẩẫậắằẳẵặẹẻẽếềểễệỉịọỏốồổỗộớờởỡợụủứừửữựỳỵỷỹ",
118
+ el: "αβγδεζηθικλμνξοπρστυφχψωάέήίόύώΑΩ",
119
+ ru: "абвгдеёжзийклмнопрстуфхцчшщъыьэюяАЯ",
120
+ uk: "абвгґдеєжзиіїйклмнопрстуфхцчшщьюяАЯ",
121
+ ar: "ابتثجحخدذرزسشصضطظعغفقكلمنهوي",
122
+ he: "אבגדהוזחטיכלמנסעפצקרשת",
123
+ hi: "अआइईउऊएऐओऔकखगघचछजझटठडढणतथदधनपफबभमयरलवशषसह",
124
+ th: "กขคงจฉชซญดตถทนบปผพฟภมยรลวสหอฮ",
125
+ ja: "あいうえおかきくけこアイウエオ日本語",
126
+ zh: "的一是不了人我在有他这中大来上国个到说们为子和你地出道也时年",
127
+ ko: "가나다라마바사아자차카타파하한국어",
128
+ };
129
+ export function languageLetters(code) {
130
+ const info = languageInfo(code);
131
+ const letters = `${info.script === "latin" ? "abcdefghijklmnopqrstuvwxyz" : ""}${extraLetters[info.code] ?? ""}`;
132
+ return [...new Set(Array.from(letters, (letter) => letter.codePointAt(0) ?? 0))];
133
+ }
134
+ // Whether a codepoint belongs to a writing system; used to decide whether a caption font
135
+ // covers the project's letters without reading the font for every character.
136
+ export function writingSystemOf(codepoint) {
137
+ const inRange = (from, to) => codepoint >= from && codepoint <= to;
138
+ if (inRange(0x41, 0x5a) || inRange(0x61, 0x7a) || inRange(0xc0, 0x24f) || inRange(0x1e00, 0x1eff))
139
+ return "latin";
140
+ if (inRange(0x370, 0x3ff) || inRange(0x1f00, 0x1fff))
141
+ return "greek";
142
+ if (inRange(0x400, 0x52f))
143
+ return "cyrillic";
144
+ if (inRange(0x590, 0x5ff))
145
+ return "hebrew";
146
+ if (inRange(0x600, 0x6ff) || inRange(0x750, 0x77f) || inRange(0xfb50, 0xfdff))
147
+ return "arabic";
148
+ if (inRange(0x900, 0x97f))
149
+ return "devanagari";
150
+ if (inRange(0xe00, 0xe7f))
151
+ return "thai";
152
+ if (inRange(0x1100, 0x11ff) || inRange(0xac00, 0xd7af))
153
+ return "hangul";
154
+ if (inRange(0x3040, 0x30ff) || inRange(0x3400, 0x4dbf) || inRange(0x4e00, 0x9fff))
155
+ return "cjk";
156
+ return undefined;
157
+ }
@@ -7,15 +7,17 @@ export function readinessIsUsable(readiness) {
7
7
  : readiness.installed && readiness.issue === undefined;
8
8
  }
9
9
  export const providerErrorKinds = [
10
+ // A key the provider rejected. Like an absent one it fails at once: no wait fixes it.
10
11
  "auth",
11
- // Distinct from `auth`, a key the provider rejected: a bad key runs the whole retry
12
- // policy, an absent one fails immediately.
13
12
  "missing_key",
14
13
  "unavailable",
15
14
  "rate_limit",
16
15
  "refusal",
17
16
  "unsupported",
18
17
  "timeout",
18
+ // The connection, the provider's server or the CLI process went away before an answer:
19
+ // nothing says the request itself was wrong, so it is worth sending again later.
20
+ "dropped",
19
21
  "other",
20
22
  ];
21
23
  export function providerError(init) {
@@ -23,6 +25,7 @@ export function providerError(init) {
23
25
  fault: {
24
26
  kind: init.kind,
25
27
  ...(init.retryAfterMs === undefined ? {} : { retryAfterMs: init.retryAfterMs }),
28
+ ...(init.planLimit === undefined ? {} : { planLimit: init.planLimit }),
26
29
  },
27
30
  });
28
31
  }
@@ -0,0 +1,60 @@
1
+ // Narration aliases: a written word or phrase and how the narrator should say it ("Dr." as
2
+ // "Doctor"). They change only what is sent to the voice (and to Narration Preparation);
3
+ // the article, the transcript and the captions keep the written form. The same matcher runs
4
+ // where the text is narrated and where captions are timed against that narration, so both
5
+ // agree on which words were said differently. It lives with the ports because the caption
6
+ // aligner (an adapter, which may import only ports) needs it as much as the recipes do.
7
+ export const aliasWrittenMax = 200;
8
+ export const aliasSpokenMax = 500;
9
+ export const aliasCountMax = 1000;
10
+ const wordCharacter = /[\p{L}\p{M}\p{N}_]/u;
11
+ // Every place an alias applies: leftmost first, the longest written form where two start at
12
+ // the same place, never overlapping.
13
+ export function aliasMatches(text, aliases) {
14
+ const found = [];
15
+ for (const alias of aliases) {
16
+ const written = alias.written.trim();
17
+ if (written === "")
18
+ continue;
19
+ const pattern = written
20
+ .split(/\s+/u)
21
+ .map((word) => word.replace(/[|\\{}()[\]^$+*?.]/gu, "\\$&"))
22
+ .join("\\s+");
23
+ const expression = new RegExp(pattern, alias.caseSensitive ? "gu" : "giu");
24
+ const first = Array.from(written)[0] ?? "";
25
+ const last = Array.from(written).at(-1) ?? "";
26
+ for (const match of text.matchAll(expression)) {
27
+ const start = match.index;
28
+ const end = start + match[0].length;
29
+ if (alias.wholeWord) {
30
+ const before = Array.from(text.slice(Math.max(0, start - 2), start)).at(-1) ?? "";
31
+ const after = Array.from(text.slice(end, end + 2))[0] ?? "";
32
+ if (wordCharacter.test(first) && wordCharacter.test(before))
33
+ continue;
34
+ if (wordCharacter.test(last) && wordCharacter.test(after))
35
+ continue;
36
+ }
37
+ found.push({ start, end, spoken: alias.spoken });
38
+ }
39
+ }
40
+ found.sort((a, b) => a.start - b.start || b.end - a.end);
41
+ const chosen = [];
42
+ let reached = 0;
43
+ for (const match of found) {
44
+ if (match.start < reached)
45
+ continue;
46
+ chosen.push(match);
47
+ reached = match.end;
48
+ }
49
+ return chosen;
50
+ }
51
+ // The same text as the narrator should read it.
52
+ export function applyAliases(text, aliases) {
53
+ let out = "";
54
+ let at = 0;
55
+ for (const match of aliasMatches(text, aliases)) {
56
+ out += text.slice(at, match.start) + match.spoken;
57
+ at = match.end;
58
+ }
59
+ return out + text.slice(at);
60
+ }
@@ -0,0 +1,36 @@
1
+ // The plan limits of the command-line agents: Claude Code, Codex and Gemini are billed to
2
+ // the user's subscription, which gives them a share of a 5-hour and a weekly allowance rather
3
+ // than a bill. Only what a CLI reports is ever recorded here; nothing is estimated.
4
+ // The subscription a CLI provider draws on. Codex text and Codex images share one login and
5
+ // so one allowance.
6
+ export const planAccounts = ["claude-code", "codex", "gemini"];
7
+ export function planAccountOf(provider) {
8
+ if (provider === "claude-code")
9
+ return "claude-code";
10
+ if (provider === "codex" || provider === "codex-image")
11
+ return "codex";
12
+ if (provider === "gemini")
13
+ return "gemini";
14
+ return undefined;
15
+ }
16
+ export const planAccountNames = {
17
+ "claude-code": "Claude",
18
+ codex: "Codex",
19
+ gemini: "Gemini",
20
+ };
21
+ // A window of `minutes` length, named the way the screens name it.
22
+ export function windowKindOf(minutes) {
23
+ if (minutes === 300)
24
+ return "five_hour";
25
+ if (minutes === 10_080)
26
+ return "weekly";
27
+ return "other";
28
+ }
29
+ // Epoch seconds or milliseconds to ISO, null for anything that is not a plausible time.
30
+ export function isoOfEpoch(value) {
31
+ if (value === null || value === undefined || !Number.isFinite(value) || value <= 0)
32
+ return null;
33
+ const ms = value < 1e12 ? value * 1000 : value;
34
+ const date = new Date(ms);
35
+ return Number.isNaN(date.getTime()) ? null : date.toISOString();
36
+ }
@@ -14,15 +14,18 @@ export const timeoutMs = {
14
14
  video: 900_000,
15
15
  };
16
16
  // A refusal and an unsupported capability are the provider's final answer; retrying only
17
- // spends the user's money again. The third is a key removed mid-run, which leaves nothing
18
- // to call with. A key the provider rejected is different: that is an `auth` failure,
19
- // retried like any other.
17
+ // spends the user's money again. A key the provider rejected, or one removed mid-run, needs
18
+ // the user in Settings before any call can succeed.
20
19
  const terminalKinds = [
21
20
  "refusal",
22
21
  "unsupported",
22
+ "auth",
23
23
  "missing_key",
24
24
  "unavailable",
25
25
  ];
26
+ // A Retry-After longer than this is not slept through with the stage holding its provider
27
+ // slot: the call fails here and the runner's persisted wait (`retry-policy.ts`) takes over.
28
+ export const inCallRetryAfterCeilingMs = 60_000;
26
29
  export async function attempt(ctx, call, opts) {
27
30
  ctx.signal.throwIfAborted();
28
31
  const limit = opts.timeoutMs ?? timeoutMs[opts.kind];
@@ -84,7 +87,12 @@ export async function attempt(ctx, call, opts) {
84
87
  stage: ctx.stage,
85
88
  detail: `${opts.kind} attempt ${n} of ${attemptLimit} failed (${failure.fault.kind}): ${failure.message}`,
86
89
  });
87
- if (terminalKinds.includes(failure.fault.kind) || n >= attemptLimit) {
90
+ // A used-up plan allowance is not retried here: the call waits for the reset outside the
91
+ // provider queue (providers.ts), so a waiting stage holds no slot other work needs.
92
+ if (terminalKinds.includes(failure.fault.kind) ||
93
+ failure.fault.planLimit !== undefined ||
94
+ n >= attemptLimit ||
95
+ (failure.fault.retryAfterMs ?? 0) > inCallRetryAfterCeilingMs) {
88
96
  throw failure;
89
97
  }
90
98
  // A 429 that names a Retry-After replaces the fixed wait with the provider's.
@@ -102,8 +110,8 @@ function classify(error, expired, opts, limit) {
102
110
  return providerError({
103
111
  kind: "timeout",
104
112
  message: opts.streaming === true
105
- ? `The ${callee[opts.kind]} sent nothing for ${seconds} seconds, so Slopify stopped waiting. It may be overloaded: wait a few minutes, then use Retry stage.`
106
- : `The ${callee[opts.kind]} did not answer within ${seconds} seconds. It may be overloaded: wait a few minutes, then use Retry stage, or choose another model in the Providers section of Edit project.`,
113
+ ? `The ${callee[opts.kind]} sent nothing for ${seconds} seconds, so Slopify stopped waiting. It may be overloaded: wait a few minutes, then use Try again.`
114
+ : `The ${callee[opts.kind]} did not answer within ${seconds} seconds. It may be overloaded: wait a few minutes, then use Try again, or choose another model in the Providers section of Edit project.`,
107
115
  });
108
116
  }
109
117
  if (isProviderError(error)) {
@@ -113,14 +121,15 @@ function classify(error, expired, opts, limit) {
113
121
  kind: error.fault.kind,
114
122
  message: redact(error.message),
115
123
  ...(error.fault.retryAfterMs === undefined ? {} : { retryAfterMs: error.fault.retryAfterMs }),
124
+ ...(error.fault.planLimit === undefined ? {} : { planLimit: error.fault.planLimit }),
116
125
  });
117
126
  }
118
127
  if (unreachable(error)) {
119
128
  // `fetch` names only itself; the cause's code (ENOTFOUND, ECONNREFUSED) is the clue.
120
129
  const code = causeCode(error);
121
130
  return providerError({
122
- kind: "other",
123
- message: `Slopify could not reach the ${callee[opts.kind]} over the internet${code === undefined ? "" : ` (${code})`}. Check your internet connection, firewall or VPN, then use Retry stage.`,
131
+ kind: "dropped",
132
+ message: `Slopify could not reach the ${callee[opts.kind]} over the internet${code === undefined ? "" : ` (${code})`}. Check your internet connection, firewall or VPN, then use Try again.`,
124
133
  });
125
134
  }
126
135
  return providerError({
@@ -0,0 +1,36 @@
1
+ import { attempt } from "./attempt.js";
2
+ const unrecorded = {
3
+ start: () => "audition",
4
+ end: () => { },
5
+ };
6
+ export function auditionVoice(deps, call, signal) {
7
+ const port = deps.registry.tts(call.provider);
8
+ return attempt({
9
+ work: {
10
+ projectId: "audition",
11
+ revisionId: "audition",
12
+ workId: "audition",
13
+ stageId: "audition",
14
+ kind: "audio",
15
+ fingerprint: "audition",
16
+ },
17
+ maySubmit: () => true,
18
+ clock: deps.clock,
19
+ log: deps.log,
20
+ attempts: unrecorded,
21
+ projectId: "audition",
22
+ stage: "audio",
23
+ stageId: "audition",
24
+ signal,
25
+ }, async (inner, progress) => {
26
+ const spoken = await port.synthesize({
27
+ model: call.model,
28
+ voiceId: call.voiceId,
29
+ text: call.text,
30
+ signal: inner,
31
+ onActivity: progress,
32
+ });
33
+ const bytes = new Uint8Array(await new Response(spoken.audio).arrayBuffer());
34
+ return { bytes, container: spoken.container };
35
+ }, { kind: "tts", streaming: port.capabilities.streams });
36
+ }
@@ -1,33 +1,39 @@
1
1
  // Images use saved prompts and can run immediately. A thumbnail's source and an
2
2
  // audio-only export narrow these dependencies through dependenciesOf(). The document is
3
3
  // laid out from the article alone, with the thumbnail as its cover when there is one, so it
4
- // runs beside narration and images rather than after them.
4
+ // runs beside narration and images rather than after them. The video never reads the
5
+ // thumbnail, so a thumbnail that fails does not hold the video back.
5
6
  export const deps = {
6
7
  research: [],
7
8
  article: ["research"],
8
9
  audio: ["article"],
9
10
  images: [],
10
11
  thumbnail: ["article"],
11
- video: ["article", "audio", "images", "thumbnail"],
12
+ video: ["article", "audio", "images"],
12
13
  document: ["article", "thumbnail"],
13
14
  };
14
15
  // `provided` and `skipped` release a dependency exactly as `done` does.
15
16
  export function satisfied(state) {
16
17
  return state === "done" || state === "provided" || state === "skipped";
17
18
  }
19
+ // What a run is for, in order: the first of these the run asks for is the one that has to be
20
+ // there for the run to count as finished. Anything else failing leaves it done with
21
+ // problems rather than failed.
22
+ const headline = ["video", "audio", "article"];
18
23
  // Stage state is derived; an explicit persisted pause takes precedence while calls
19
- // drain. Completed independent images do not imply that a run was canceled.
24
+ // drain. Completed independent images do not imply that a run was canceled. A stage
25
+ // waiting out a rate limit is still part of a run in progress.
20
26
  export function derive(stages, paused = false) {
21
27
  if (paused)
22
28
  return "paused";
23
- if (stages.some((stage) => stage.state === "running")) {
29
+ if (stages.some((stage) => stage.state === "running" || waiting(stage))) {
24
30
  return "running";
25
31
  }
26
32
  if (stages.some((stage) => stage.state === "canceled")) {
27
33
  return "canceled";
28
34
  }
29
35
  if (stages.some((stage) => stage.state === "failed")) {
30
- return "failed";
36
+ return headlineMade(stages) ? "partial" : "failed";
31
37
  }
32
38
  if (stages.length > 0 && stages.every((stage) => satisfied(stage.state))) {
33
39
  return "done";
@@ -35,6 +41,19 @@ export function derive(stages, paused = false) {
35
41
  // Includes a run created but not yet claimed and unfinished work after restart.
36
42
  return "pending";
37
43
  }
44
+ function waiting(stage) {
45
+ return stage.state === "pending" && stage.retryAt !== undefined && stage.retryAt !== null;
46
+ }
47
+ function headlineMade(stages) {
48
+ for (const kind of headline) {
49
+ const stage = stages.find((one) => one.kind === kind);
50
+ if (stage === undefined || stage.state === "skipped")
51
+ continue;
52
+ // A file the user supplied is not something this run made.
53
+ return stage.state === "done";
54
+ }
55
+ return false;
56
+ }
38
57
  export function dependenciesOf(kind, sources) {
39
58
  if (kind === "thumbnail" && sources.thumbnail === "from_prompt")
40
59
  return [];
@@ -1,6 +1,7 @@
1
1
  import { causedBy } from "../errors.js";
2
2
  import { derive, deps as graph, satisfied } from "./graph.js";
3
3
  import { progressGate } from "./progress.js";
4
+ import { faultOf } from "./retry-policy.js";
4
5
  const pauseReason = new Error("paused by user");
5
6
  export function createRunner(deps) {
6
7
  const inflight = new Map();
@@ -15,7 +16,7 @@ export function createRunner(deps) {
15
16
  const stopped = new Set();
16
17
  let count = 0;
17
18
  let shuttingDown = false;
18
- const emitStage = (stage, state, reason) => {
19
+ const emitStage = (stage, state, reason, detail = {}) => {
19
20
  deps.emit(stage.projectId, {
20
21
  type: "stage.state",
21
22
  revisionId: stage.work.revisionId,
@@ -24,6 +25,8 @@ export function createRunner(deps) {
24
25
  stage: stage.kind,
25
26
  state,
26
27
  ...(reason === null ? {} : { failureReason: reason }),
28
+ ...(detail.retryAt === undefined ? {} : { retryAt: detail.retryAt }),
29
+ ...(detail.failureKind === undefined ? {} : { failureKind: detail.failureKind }),
27
30
  });
28
31
  };
29
32
  // The global tally counts projects with a stage in flight, read off the in-flight set
@@ -46,9 +49,9 @@ export function createRunner(deps) {
46
49
  announced.set(projectId, state);
47
50
  deps.emit(projectId, { type: "project.state", projectId, state });
48
51
  };
49
- const conclude = (stage, state, reason) => {
52
+ const conclude = (stage, state, reason, kind) => {
50
53
  try {
51
- deps.stages.finish(stage.work, state, reason);
54
+ deps.stages.finish(stage.work, state, reason, kind ?? null);
52
55
  }
53
56
  catch (error) {
54
57
  // The row stays `running` when the write fails. The event still goes out, and the
@@ -59,7 +62,31 @@ export function createRunner(deps) {
59
62
  detail: reasonOf(error),
60
63
  });
61
64
  }
62
- emitStage(stage, state, reason);
65
+ emitStage(stage, state, reason, { failureKind: kind });
66
+ };
67
+ // False when the failure is to be shown now.
68
+ const deferred = (stage, fault, reason) => {
69
+ let at;
70
+ try {
71
+ at = deps.stages.waitToRetry?.(stage.work, fault, reason);
72
+ }
73
+ catch (error) {
74
+ deps.log.write("error", "stage.retry", {
75
+ projectId: stage.projectId,
76
+ stage: stage.kind,
77
+ detail: reasonOf(error),
78
+ });
79
+ return false;
80
+ }
81
+ if (at === undefined)
82
+ return false;
83
+ deps.log.write("warn", "stage.retry", {
84
+ projectId: stage.projectId,
85
+ stage: stage.kind,
86
+ detail: `waiting until ${at} to run again after ${fault?.kind ?? "a failure"}: ${reason}`,
87
+ });
88
+ emitStage(stage, "pending", reason, { retryAt: at, failureKind: fault?.kind });
89
+ return true;
63
90
  };
64
91
  async function execute(stage, controller) {
65
92
  const tag = (event) => ({
@@ -103,12 +130,15 @@ export function createRunner(deps) {
103
130
  conclude(stage, paused ? "pending" : "canceled", paused ? null : "canceled by user");
104
131
  return;
105
132
  }
133
+ const fault = faultOf(error);
134
+ if (deferred(stage, fault, reasonOf(error)))
135
+ return;
106
136
  deps.log.write("error", "stage.failed", {
107
137
  projectId: stage.projectId,
108
138
  stage: stage.kind,
109
139
  detail: causedBy(error),
110
140
  });
111
- conclude(stage, "failed", reasonOf(error));
141
+ conclude(stage, "failed", reasonOf(error), fault?.kind);
112
142
  }
113
143
  }
114
144
  function start(stage) {
@@ -0,0 +1 @@
1
+ export {};