@gentbajko/slopify 3.25.9 → 4.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (718) hide show
  1. package/dist/adapters/account-limits.js +30 -0
  2. package/dist/adapters/alignment/audio.js +1 -1
  3. package/dist/adapters/alignment/cache.js +4 -6
  4. package/dist/adapters/alignment/ctc.js +1 -2
  5. package/dist/adapters/alignment/lock.js +4 -3
  6. package/dist/adapters/alignment/multilingual.js +2 -4
  7. package/dist/adapters/alignment/prefetch.js +2 -1
  8. package/dist/adapters/alignment/runner.js +1 -1
  9. package/dist/adapters/alignment/worker.js +3 -2
  10. package/dist/adapters/explain.js +10 -12
  11. package/dist/adapters/ffmpeg.js +19 -1
  12. package/dist/adapters/host-cli/index.js +21 -4
  13. package/dist/adapters/host-cli/transport.js +2 -1
  14. package/dist/adapters/http-failure.js +53 -0
  15. package/dist/adapters/image/bytes.js +2 -1
  16. package/dist/adapters/image/codex.js +42 -9
  17. package/dist/adapters/image/fal.js +21 -53
  18. package/dist/adapters/image/google.js +33 -56
  19. package/dist/adapters/image/models.js +2 -2
  20. package/dist/adapters/image/openai.js +22 -48
  21. package/dist/adapters/image/reference.js +36 -0
  22. package/dist/adapters/image/replicate.js +21 -49
  23. package/dist/adapters/image/video.js +2 -1
  24. package/dist/adapters/interest/search-interest.js +113 -0
  25. package/dist/adapters/key-probes.js +54 -3
  26. package/dist/adapters/llm/antigravity-models.js +1 -1
  27. package/dist/adapters/llm/antigravity.js +11 -6
  28. package/dist/adapters/llm/assistant-cli.js +388 -0
  29. package/dist/adapters/llm/child-session.js +83 -0
  30. package/dist/adapters/llm/claude-code-models.js +52 -99
  31. package/dist/adapters/llm/claude-code.js +16 -4
  32. package/dist/adapters/llm/codex-limits.js +27 -68
  33. package/dist/adapters/llm/codex-models.js +1 -1
  34. package/dist/adapters/llm/codex-server-models.js +61 -109
  35. package/dist/adapters/llm/codex.js +12 -1
  36. package/dist/adapters/llm/document-workspace.js +3 -2
  37. package/dist/adapters/llm/image-workspace.js +3 -2
  38. package/dist/adapters/llm/openrouter.js +12 -48
  39. package/dist/adapters/llm/run-cli.js +9 -24
  40. package/dist/adapters/llm/sse-lines.js +5 -1
  41. package/dist/adapters/model-access.js +37 -0
  42. package/dist/adapters/tts/cartesia.js +15 -46
  43. package/dist/adapters/tts/elevenlabs.js +20 -52
  44. package/dist/adapters/tts/gemini.js +21 -41
  45. package/dist/adapters/tts/inworld-async.js +6 -2
  46. package/dist/adapters/tts/inworld.js +6 -2
  47. package/dist/adapters/tts/openai.js +20 -50
  48. package/dist/adapters/tts/system.js +2 -1
  49. package/dist/assets/models-lifecycle.yaml +6 -0
  50. package/dist/assistant-wiring.js +60 -0
  51. package/dist/autopilot-timers.js +59 -0
  52. package/dist/catalog/lifecycle.js +52 -0
  53. package/dist/catalog/propose.js +204 -0
  54. package/dist/catalog/registry.js +23 -14
  55. package/dist/catalog/schema.js +4 -1
  56. package/dist/catalog/store.js +39 -0
  57. package/dist/catalog/validate.js +5 -2
  58. package/dist/edge/autostart/native.js +3 -5
  59. package/dist/edge/autostart/prompt.js +2 -1
  60. package/dist/edge/autostart/service.js +3 -4
  61. package/dist/edge/cli-args.js +3 -4
  62. package/dist/edge/cli.js +2 -1
  63. package/dist/edge/docker-install/apply.js +11 -11
  64. package/dist/edge/docker-install/run.js +2 -1
  65. package/dist/edge/docker-install/state.js +4 -3
  66. package/dist/edge/docker-install/tree.js +2 -4
  67. package/dist/edge/docker-install/volume.js +3 -2
  68. package/dist/edge/docker.js +5 -4
  69. package/dist/edge/events/hub.js +5 -5
  70. package/dist/edge/http/actions.js +11 -32
  71. package/dist/edge/http/actor.js +5 -3
  72. package/dist/edge/http/app.js +86 -11
  73. package/dist/edge/http/assistant.js +129 -0
  74. package/dist/edge/http/audio-preview.js +4 -11
  75. package/dist/edge/http/auditions.js +1 -1
  76. package/dist/edge/http/autostart.js +5 -5
  77. package/dist/edge/http/backups.js +2 -4
  78. package/dist/edge/http/channel-memory.js +9 -22
  79. package/dist/edge/http/channels.js +14 -27
  80. package/dist/edge/http/checkpoints.js +39 -21
  81. package/dist/edge/http/design-templates.js +167 -0
  82. package/dist/edge/http/designs.js +178 -0
  83. package/dist/edge/http/diagnostics.js +5 -1
  84. package/dist/edge/http/document-themes.js +5 -14
  85. package/dist/edge/http/draft-files.js +1 -3
  86. package/dist/edge/http/draft-problem.js +5 -10
  87. package/dist/edge/http/drafts.js +3 -3
  88. package/dist/edge/http/entries.js +6 -9
  89. package/dist/edge/http/extension-auth.js +27 -0
  90. package/dist/edge/http/external-reviews.js +12 -11
  91. package/dist/edge/http/files.js +5 -11
  92. package/dist/edge/http/folder-location.js +1 -4
  93. package/dist/edge/http/fonts.js +0 -7
  94. package/dist/edge/http/home.js +47 -14
  95. package/dist/edge/http/host-cli.js +18 -8
  96. package/dist/edge/http/ids.js +8 -0
  97. package/dist/edge/http/keywords.js +35 -0
  98. package/dist/edge/http/model-checks.js +132 -0
  99. package/dist/edge/http/narration-peaks.js +5 -13
  100. package/dist/edge/http/onboarding.js +30 -26
  101. package/dist/edge/http/open-folder.js +5 -10
  102. package/dist/edge/http/outputs.js +7 -14
  103. package/dist/edge/http/patch-notes.js +6 -12
  104. package/dist/edge/http/planning.js +3 -7
  105. package/dist/edge/http/problem.js +2 -4
  106. package/dist/edge/http/project-create.js +2 -1
  107. package/dist/edge/http/project-templates.js +31 -19
  108. package/dist/edge/http/projects.js +24 -16
  109. package/dist/edge/http/prompts.js +9 -16
  110. package/dist/edge/http/pronunciations.js +1 -2
  111. package/dist/edge/http/providers.js +48 -34
  112. package/dist/edge/http/refusal.js +29 -0
  113. package/dist/edge/http/remote-access.js +40 -0
  114. package/dist/edge/http/remote-guard.js +71 -0
  115. package/dist/edge/http/request-guard.js +91 -0
  116. package/dist/edge/http/reviews.js +12 -25
  117. package/dist/edge/http/revision-files.js +6 -11
  118. package/dist/edge/http/revisions.js +14 -19
  119. package/dist/edge/http/run-cost.js +1 -2
  120. package/dist/edge/http/same-page.js +80 -0
  121. package/dist/edge/http/schedules.js +69 -40
  122. package/dist/edge/http/settings.js +27 -14
  123. package/dist/edge/http/social.js +283 -0
  124. package/dist/edge/http/staging.js +3 -5
  125. package/dist/edge/http/storage-files.js +5 -7
  126. package/dist/edge/http/storage.js +33 -25
  127. package/dist/edge/http/studio.js +94 -69
  128. package/dist/edge/http/style-preview.js +3 -6
  129. package/dist/edge/http/subtitles.js +3 -17
  130. package/dist/edge/http/template-studio.js +436 -0
  131. package/dist/edge/http/trash.js +11 -12
  132. package/dist/edge/http/tutorial.js +1 -2
  133. package/dist/edge/http/tutorials.js +5 -11
  134. package/dist/edge/http/update.js +7 -13
  135. package/dist/edge/http/waveform.js +3 -6
  136. package/dist/edge/http/youtube-edits.js +3 -4
  137. package/dist/edge/mcp/assistant.js +279 -0
  138. package/dist/edge/mcp/attribution.js +8 -2
  139. package/dist/edge/mcp/call-route.js +11 -2
  140. package/dist/edge/mcp/excluded-routes.js +29 -3
  141. package/dist/edge/mcp/http.js +11 -2
  142. package/dist/edge/mcp/instructions.js +6 -4
  143. package/dist/edge/mcp/library-tools.js +9 -5
  144. package/dist/edge/mcp/remake-tools.js +7 -4
  145. package/dist/edge/mcp/route-tool.js +35 -15
  146. package/dist/edge/mcp/server.js +13 -8
  147. package/dist/edge/mcp/tools-channels.js +11 -9
  148. package/dist/edge/mcp/tools-designs.js +165 -0
  149. package/dist/edge/mcp/tools-library.js +19 -7
  150. package/dist/edge/mcp/tools-planning.js +40 -21
  151. package/dist/edge/mcp/tools-projects.js +38 -11
  152. package/dist/edge/mcp/tools-publishing.js +9 -9
  153. package/dist/edge/mcp/tools-runs.js +23 -10
  154. package/dist/edge/mcp/tools-schedules.js +32 -8
  155. package/dist/edge/mcp/tools-setup.js +75 -11
  156. package/dist/edge/mcp/tools-social.js +47 -0
  157. package/dist/edge/mcp/tools-storage.js +11 -3
  158. package/dist/edge/mcp/tools-template-studio.js +166 -0
  159. package/dist/edge/mcp/tools.js +6 -0
  160. package/dist/edge/remote-access/cli.js +71 -0
  161. package/dist/edge/remote-access/model.js +32 -0
  162. package/dist/edge/remote-access/rules.js +207 -0
  163. package/dist/edge/remote-access/service.js +203 -0
  164. package/dist/edge/remote-access/store.js +44 -0
  165. package/dist/edge/remote-access/views.js +43 -0
  166. package/dist/extension/slopify-studio-chrome.zip +0 -0
  167. package/dist/extension/slopify-studio-firefox.zip +0 -0
  168. package/dist/host-cli/install.js +2 -2
  169. package/dist/host-cli/open-folder.js +2 -2
  170. package/dist/host-cli/paths.js +2 -4
  171. package/dist/host-cli/server.js +4 -4
  172. package/dist/host-cli/service.js +3 -3
  173. package/dist/host-cli/status.js +7 -7
  174. package/dist/kernel/db/migrations/0063-model-checks.sql +20 -0
  175. package/dist/kernel/db/migrations/0064-template-studio.sql +46 -0
  176. package/dist/kernel/db/migrations/0065-promise-and-assistant-reviews.sql +58 -0
  177. package/dist/kernel/db/migrations/0066-assistant.sql +44 -0
  178. package/dist/kernel/db/migrations/0067-autopilot.sql +6 -0
  179. package/dist/kernel/db/migrations/0068-topic-scores.sql +17 -0
  180. package/dist/kernel/db/migrations/0070-designs.sql +53 -0
  181. package/dist/kernel/db/migrations/0071-design-usage.sql +33 -0
  182. package/dist/kernel/db/migrations/0072-design-templates.sql +33 -0
  183. package/dist/kernel/db/migrations/0073-schedule-design-kind.sql +5 -0
  184. package/dist/kernel/db/migrations/0074-template-bakeoffs.sql +44 -0
  185. package/dist/kernel/db/migrations/0075-social-posts.sql +25 -0
  186. package/dist/kernel/db/migrations/0076-channel-social.sql +5 -0
  187. package/dist/kernel/db/migrations/0077-post-prompts.sql +17 -0
  188. package/dist/kernel/db/migrations/0078-uploaded-from-links.sql +7 -0
  189. package/dist/kernel/db/tx.js +2 -4
  190. package/dist/kernel/errors.js +27 -1
  191. package/dist/kernel/json-answer.js +15 -0
  192. package/dist/kernel/lock.js +4 -9
  193. package/dist/kernel/paths.js +8 -0
  194. package/dist/kernel/pipeline.js +12 -0
  195. package/dist/kernel/ports/assistant-cli.js +4 -0
  196. package/dist/kernel/ports/host-cli.js +3 -0
  197. package/dist/kernel/ports/interest.js +4 -0
  198. package/dist/kernel/ports/languages.js +0 -3
  199. package/dist/kernel/ports/model.js +2 -0
  200. package/dist/kernel/ports/settings-places.js +31 -0
  201. package/dist/kernel/ports/system-speech.js +31 -3
  202. package/dist/kernel/runner/attempt.js +14 -4
  203. package/dist/kernel/runner/audition.js +3 -1
  204. package/dist/kernel/runner/checkpoint-authority.js +2 -1
  205. package/dist/kernel/runner/index.js +8 -10
  206. package/dist/kernel/runner/model-access.js +40 -0
  207. package/dist/kernel/runner/model-check.js +153 -0
  208. package/dist/kernel/runner/providers.js +18 -12
  209. package/dist/kernel/runner/standalone.js +11 -7
  210. package/dist/main.js +338 -21
  211. package/dist/model-catalog.js +20 -0
  212. package/dist/model-checks.js +166 -0
  213. package/dist/patch-notes/4.0.0.md +51 -0
  214. package/dist/patch-notes/4.1.0.md +42 -0
  215. package/dist/patch-notes/index.json +12 -0
  216. package/dist/slices/admission/heads.js +2 -1
  217. package/dist/slices/admission/listing-facts.js +95 -0
  218. package/dist/slices/admission/repo.js +11 -7
  219. package/dist/slices/admission/rules.js +50 -15
  220. package/dist/slices/admission/schema.js +8 -0
  221. package/dist/slices/admission/stage-writes.js +0 -3
  222. package/dist/slices/admission/start.js +48 -31
  223. package/dist/slices/admission/state-words.js +34 -5
  224. package/dist/slices/admission/substitute.js +7 -4
  225. package/dist/slices/agents/attention-posts.js +31 -0
  226. package/dist/slices/agents/attention-templates.js +33 -0
  227. package/dist/slices/agents/attention.js +10 -1
  228. package/dist/slices/agents/connection.js +2 -6
  229. package/dist/slices/agents/model.js +6 -3
  230. package/dist/slices/agents/repo.js +15 -7
  231. package/dist/slices/article/continuation.js +0 -63
  232. package/dist/slices/article/plain.js +3 -2
  233. package/dist/slices/article/segments.js +2 -62
  234. package/dist/slices/assistant/access.js +1 -0
  235. package/dist/slices/assistant/asks.js +80 -0
  236. package/dist/slices/assistant/cost.js +54 -0
  237. package/dist/slices/assistant/gate.js +222 -0
  238. package/dist/slices/assistant/model.js +86 -0
  239. package/dist/slices/assistant/names.js +23 -0
  240. package/dist/slices/assistant/prompt.js +74 -0
  241. package/dist/slices/assistant/repo.js +74 -0
  242. package/dist/slices/assistant/tokens.js +18 -0
  243. package/dist/slices/assistant/turn.js +344 -0
  244. package/dist/slices/autopilot/buffer.js +76 -0
  245. package/dist/slices/autopilot/model.js +14 -0
  246. package/dist/slices/autopilot/notify.js +27 -0
  247. package/dist/slices/autopilot/repo.js +34 -0
  248. package/dist/slices/autopilot/signals.js +194 -0
  249. package/dist/slices/autopilot/warnings.js +85 -0
  250. package/dist/slices/autopilot/weekly.js +100 -0
  251. package/dist/slices/backups/files.js +2 -6
  252. package/dist/slices/backups/repo.js +3 -15
  253. package/dist/slices/backups/service.js +8 -7
  254. package/dist/slices/channels/cast-images.js +3 -2
  255. package/dist/slices/channels/model.js +0 -1
  256. package/dist/slices/channels/rebrand.js +0 -3
  257. package/dist/slices/channels/repo.js +4 -0
  258. package/dist/slices/channels/schema.js +7 -1
  259. package/dist/slices/channels/service.js +10 -0
  260. package/dist/slices/channels/videos.js +3 -15
  261. package/dist/slices/checkpoints/index.js +0 -1
  262. package/dist/slices/control/providers.js +2 -28
  263. package/dist/slices/designs/artwork.js +66 -0
  264. package/dist/slices/designs/budget.js +23 -0
  265. package/dist/slices/designs/calls.js +124 -0
  266. package/dist/slices/designs/check-context.js +38 -0
  267. package/dist/slices/designs/check-text.js +43 -0
  268. package/dist/slices/designs/fields.js +88 -0
  269. package/dist/slices/designs/frames.js +34 -0
  270. package/dist/slices/designs/gate.js +130 -0
  271. package/dist/slices/designs/layout.js +293 -0
  272. package/dist/slices/designs/model.js +35 -0
  273. package/dist/slices/designs/pdf-job.js +23 -0
  274. package/dist/slices/designs/pdf-pages.js +30 -0
  275. package/dist/slices/designs/pdf-runner.js +68 -0
  276. package/dist/slices/designs/pdf-worker.js +24 -0
  277. package/dist/slices/designs/picture-size.js +79 -0
  278. package/dist/slices/designs/pictures.js +55 -0
  279. package/dist/slices/designs/quote.js +24 -0
  280. package/dist/slices/designs/render-args.js +36 -0
  281. package/dist/slices/designs/render-input.js +59 -0
  282. package/dist/slices/designs/render.js +215 -0
  283. package/dist/slices/designs/repo.js +101 -0
  284. package/dist/slices/designs/requests.js +80 -0
  285. package/dist/slices/designs/runner.js +239 -0
  286. package/dist/slices/designs/sample-repo.js +91 -0
  287. package/dist/slices/designs/sample.js +223 -0
  288. package/dist/slices/designs/schema.js +87 -0
  289. package/dist/slices/designs/sections.js +44 -0
  290. package/dist/slices/designs/service.js +233 -0
  291. package/dist/slices/designs/steps-repo.js +102 -0
  292. package/dist/slices/designs/studio-kind.js +39 -0
  293. package/dist/slices/designs/template-checks.js +44 -0
  294. package/dist/slices/designs/template-make.js +118 -0
  295. package/dist/slices/designs/templates.js +218 -0
  296. package/dist/slices/document/fonts.js +2 -1
  297. package/dist/slices/document/model.js +7 -4
  298. package/dist/slices/document/render.js +2 -1
  299. package/dist/slices/document/theme-schema.js +1 -1
  300. package/dist/slices/document/theme.js +49 -3
  301. package/dist/slices/episodes/summarize.js +6 -2
  302. package/dist/slices/estimate/index.js +72 -28
  303. package/dist/slices/estimate/requests.js +4 -4
  304. package/dist/slices/fixes/rules.js +3 -1
  305. package/dist/slices/fonts/catalog.js +5 -3
  306. package/dist/slices/fonts/coverage.js +2 -1
  307. package/dist/slices/fonts/discovery.js +3 -2
  308. package/dist/slices/fonts/files.js +2 -6
  309. package/dist/slices/fonts/index.js +0 -1
  310. package/dist/slices/fonts/sfnt.js +45 -10
  311. package/dist/slices/images/appearance.js +8 -16
  312. package/dist/slices/images/scenes.js +102 -28
  313. package/dist/slices/keywords/digest.js +85 -0
  314. package/dist/slices/keywords/facts.js +92 -0
  315. package/dist/slices/keywords/fill.js +80 -0
  316. package/dist/slices/keywords/model.js +75 -0
  317. package/dist/slices/keywords/rules.js +187 -0
  318. package/dist/slices/keywords/schema.js +28 -0
  319. package/dist/slices/keywords/specs.js +260 -0
  320. package/dist/slices/keywords/values.js +74 -0
  321. package/dist/slices/library/lint.js +29 -12
  322. package/dist/slices/library/model.js +2 -0
  323. package/dist/slices/library/photorealistic.js +4 -12
  324. package/dist/slices/library/rename.js +9 -7
  325. package/dist/slices/library/save.js +14 -10
  326. package/dist/slices/library/slots.js +37 -11
  327. package/dist/slices/library/transfer.js +5 -4
  328. package/dist/slices/library/used-by.js +25 -10
  329. package/dist/slices/loudness/loudnorm.js +53 -12
  330. package/dist/slices/loudness/model.js +3 -2
  331. package/dist/slices/loudness/sentence-level.js +2 -1
  332. package/dist/slices/model-checks/cost.js +61 -0
  333. package/dist/slices/model-checks/listing.js +124 -0
  334. package/dist/slices/model-checks/model.js +20 -0
  335. package/dist/slices/model-checks/record.js +32 -0
  336. package/dist/slices/model-checks/repo.js +120 -0
  337. package/dist/slices/model-checks/rules.js +125 -0
  338. package/dist/slices/model-checks/run.js +143 -0
  339. package/dist/slices/model-checks/service.js +198 -0
  340. package/dist/slices/model-upkeep/model.js +1 -1
  341. package/dist/slices/model-upkeep/switch.js +72 -27
  342. package/dist/slices/model-upkeep/usage.js +39 -13
  343. package/dist/slices/narration/breaths.js +3 -1
  344. package/dist/slices/narration/clean-pieces.js +3 -1
  345. package/dist/slices/narration/concat.js +2 -1
  346. package/dist/slices/narration/pauses-model.js +3 -2
  347. package/dist/slices/narration/peaks.js +3 -4
  348. package/dist/slices/narration/pronunciation-chunks.js +4 -3
  349. package/dist/slices/narration/pronunciation.js +6 -4
  350. package/dist/slices/narration/steering.js +4 -3
  351. package/dist/slices/notifications/notifier.js +4 -3
  352. package/dist/slices/notifications/rules.js +5 -2
  353. package/dist/slices/notifications/settings.js +4 -12
  354. package/dist/slices/notifications/subject.js +4 -3
  355. package/dist/slices/onboarding/copy.js +21 -3
  356. package/dist/slices/onboarding/first-run.js +16 -40
  357. package/dist/slices/onboarding/full-video.js +3 -2
  358. package/dist/slices/onboarding/install.js +6 -4
  359. package/dist/slices/onboarding/model.js +26 -37
  360. package/dist/slices/onboarding/packs.js +22 -22
  361. package/dist/slices/onboarding/preferred-model.js +14 -0
  362. package/dist/slices/onboarding/quick-short.js +105 -65
  363. package/dist/slices/onboarding/sample.js +4 -2
  364. package/dist/slices/onboarding/state.js +9 -11
  365. package/dist/slices/onboarding/template.js +8 -6
  366. package/dist/slices/outputs/add.js +3 -2
  367. package/dist/slices/outputs/service.js +2 -2
  368. package/dist/slices/patch-notes/library.js +4 -3
  369. package/dist/slices/play-drafts/convert.js +4 -0
  370. package/dist/slices/play-drafts/readiness.js +3 -3
  371. package/dist/slices/play-drafts/review-inputs.js +9 -3
  372. package/dist/slices/play-drafts/review.js +5 -4
  373. package/dist/slices/play-drafts/schema.js +4 -0
  374. package/dist/slices/play-drafts/service.js +17 -7
  375. package/dist/slices/project-templates/from-project.js +9 -2
  376. package/dist/slices/project-templates/next-chapter.js +2 -1
  377. package/dist/slices/project-templates/provider-defaults.js +22 -0
  378. package/dist/slices/project-templates/repo.js +6 -1
  379. package/dist/slices/project-templates/setup-sections.js +26 -0
  380. package/dist/slices/project-templates/setup.js +14 -1
  381. package/dist/slices/project-templates/stand-ins.js +22 -0
  382. package/dist/slices/rebuild/activity.js +2 -0
  383. package/dist/slices/rebuild/admission-repo.js +3 -2
  384. package/dist/slices/rebuild/narration-history.js +4 -5
  385. package/dist/slices/rebuild/narration-retry.js +2 -1
  386. package/dist/slices/rebuild/preview-details.js +32 -14
  387. package/dist/slices/rebuild/preview-plan.js +3 -8
  388. package/dist/slices/rebuild/recipe-build.js +8 -3
  389. package/dist/slices/rebuild/recipe-describe.js +3 -3
  390. package/dist/slices/rebuild/recipe-digest.js +58 -0
  391. package/dist/slices/rebuild/recipe-input-schema.js +10 -0
  392. package/dist/slices/rebuild/recipe-keywords.js +71 -0
  393. package/dist/slices/rebuild/recipe-legacy.js +5 -1
  394. package/dist/slices/rebuild/recipe-model.js +4 -0
  395. package/dist/slices/rebuild/recipe-narration-text.js +2 -1
  396. package/dist/slices/rebuild/recipe-preparation.js +4 -4
  397. package/dist/slices/rebuild/recipe-reference.js +8 -4
  398. package/dist/slices/rebuild/recipe-reviews.js +27 -7
  399. package/dist/slices/rebuild/recipe-save.js +8 -5
  400. package/dist/slices/rebuild/recipe-scenes.js +40 -11
  401. package/dist/slices/rebuild/recipe-shorts.js +8 -8
  402. package/dist/slices/rebuild/recipe-social.js +46 -0
  403. package/dist/slices/rebuild/recipe-text.js +64 -20
  404. package/dist/slices/rebuild/recipe-validation.js +102 -31
  405. package/dist/slices/rebuild/recipe-visual.js +55 -16
  406. package/dist/slices/rebuild/recipe-voices.js +3 -2
  407. package/dist/slices/rebuild/recipe-work.js +19 -7
  408. package/dist/slices/rebuild/recipe-youtube.js +4 -3
  409. package/dist/slices/rebuild/recovery-selection.js +3 -1
  410. package/dist/slices/rebuild/recovery.js +9 -8
  411. package/dist/slices/rebuild/remake-outdated.js +5 -5
  412. package/dist/slices/rebuild/repo.js +4 -3
  413. package/dist/slices/rebuild/review-promise.js +25 -0
  414. package/dist/slices/rebuild/review-redo.js +3 -2
  415. package/dist/slices/rebuild/runtime-admission.js +4 -3
  416. package/dist/slices/rebuild/runtime-agent-review.js +11 -5
  417. package/dist/slices/rebuild/runtime-animate.js +10 -9
  418. package/dist/slices/rebuild/runtime-article.js +7 -5
  419. package/dist/slices/rebuild/runtime-checkpoints.js +5 -4
  420. package/dist/slices/rebuild/runtime-document.js +11 -13
  421. package/dist/slices/rebuild/runtime-export-bed.js +8 -12
  422. package/dist/slices/rebuild/runtime-export-edit.js +8 -16
  423. package/dist/slices/rebuild/runtime-export-inputs.js +12 -11
  424. package/dist/slices/rebuild/runtime-export-short.js +9 -5
  425. package/dist/slices/rebuild/runtime-export.js +38 -15
  426. package/dist/slices/rebuild/runtime-figure-card.js +2 -1
  427. package/dist/slices/rebuild/runtime-image.js +8 -12
  428. package/dist/slices/rebuild/runtime-instructions.js +2 -0
  429. package/dist/slices/rebuild/runtime-keywords.js +59 -0
  430. package/dist/slices/rebuild/runtime-lines.js +2 -11
  431. package/dist/slices/rebuild/runtime-local.js +17 -22
  432. package/dist/slices/rebuild/runtime-narration-publication.js +2 -5
  433. package/dist/slices/rebuild/runtime-narration-reuse.js +9 -13
  434. package/dist/slices/rebuild/runtime-narration-text.js +13 -17
  435. package/dist/slices/rebuild/runtime-pictures.js +2 -4
  436. package/dist/slices/rebuild/runtime-piece-label.js +10 -6
  437. package/dist/slices/rebuild/runtime-plan.js +31 -5
  438. package/dist/slices/rebuild/runtime-provider.js +67 -25
  439. package/dist/slices/rebuild/runtime-publication.js +10 -9
  440. package/dist/slices/rebuild/runtime-review.js +39 -33
  441. package/dist/slices/rebuild/runtime-run.js +4 -1
  442. package/dist/slices/rebuild/runtime-shorts.js +31 -50
  443. package/dist/slices/rebuild/runtime-store.js +20 -10
  444. package/dist/slices/rebuild/runtime-subtitles.js +28 -25
  445. package/dist/slices/rebuild/runtime-voices.js +10 -16
  446. package/dist/slices/rebuild/runtime-youtube.js +14 -31
  447. package/dist/slices/rebuild/service-readiness.js +11 -3
  448. package/dist/slices/rebuild/transition-repo.js +6 -5
  449. package/dist/slices/rebuild/work-records.js +6 -0
  450. package/dist/slices/reruns/cascade.js +1 -2
  451. package/dist/slices/reruns/index.js +6 -9
  452. package/dist/slices/research/synthesis.js +3 -2
  453. package/dist/slices/reviews/external-model.js +4 -1
  454. package/dist/slices/reviews/external-repo.js +2 -1
  455. package/dist/slices/reviews/external-rows.js +2 -3
  456. package/dist/slices/reviews/holds.js +5 -4
  457. package/dist/slices/reviews/model.js +32 -11
  458. package/dist/slices/reviews/repo.js +2 -1
  459. package/dist/slices/reviews/rules.js +4 -3
  460. package/dist/slices/reviews/verdict.js +2 -1
  461. package/dist/slices/reviews/view.js +5 -2
  462. package/dist/slices/revisions/downloads.js +4 -5
  463. package/dist/slices/revisions/image-plan.js +1 -1
  464. package/dist/slices/revisions/mutation-assets.js +9 -5
  465. package/dist/slices/revisions/mutation-cues.js +2 -5
  466. package/dist/slices/revisions/mutations.js +3 -3
  467. package/dist/slices/revisions/repo.js +3 -1
  468. package/dist/slices/revisions/schema.js +1 -0
  469. package/dist/slices/revisions/view.js +20 -0
  470. package/dist/slices/run-cost/limits.js +5 -2
  471. package/dist/slices/run-cost/meter.js +12 -5
  472. package/dist/slices/run-cost/panel.js +8 -74
  473. package/dist/slices/run-cost/plan-readings.js +127 -0
  474. package/dist/slices/run-cost/plan-use.js +169 -0
  475. package/dist/slices/run-cost/pricing.js +3 -3
  476. package/dist/slices/run-cost/week.js +32 -4
  477. package/dist/slices/schedules/agenda.js +18 -7
  478. package/dist/slices/schedules/by-date.js +42 -0
  479. package/dist/slices/schedules/calendar.js +5 -0
  480. package/dist/slices/schedules/design-agenda.js +36 -0
  481. package/dist/slices/schedules/design-check.js +41 -0
  482. package/dist/slices/schedules/design-run.js +126 -0
  483. package/dist/slices/schedules/next-run.js +62 -0
  484. package/dist/slices/schedules/prepare.js +18 -2
  485. package/dist/slices/schedules/repo.js +30 -21
  486. package/dist/slices/schedules/rescore.js +35 -0
  487. package/dist/slices/schedules/scheduler.js +13 -3
  488. package/dist/slices/schedules/schema.js +57 -3
  489. package/dist/slices/schedules/score.js +107 -0
  490. package/dist/slices/schedules/service.js +57 -24
  491. package/dist/slices/schedules/topic-list.js +15 -0
  492. package/dist/slices/schedules/topic-scores.js +104 -0
  493. package/dist/slices/schedules/topics.js +53 -41
  494. package/dist/slices/settings/cli-paths.js +4 -6
  495. package/dist/slices/settings/first-run.js +8 -43
  496. package/dist/slices/settings/health.js +30 -15
  497. package/dist/slices/settings/key-test.js +1 -1
  498. package/dist/slices/settings/model-reach.js +3 -17
  499. package/dist/slices/settings/model.js +14 -2
  500. package/dist/slices/settings/playback.js +34 -23
  501. package/dist/slices/settings/probe-read.js +16 -0
  502. package/dist/slices/settings/repo.js +33 -0
  503. package/dist/slices/settings/tutorial.js +2 -2
  504. package/dist/slices/settings/voices.js +4 -12
  505. package/dist/slices/shorts/keep.js +2 -1
  506. package/dist/slices/shorts/pick.js +6 -18
  507. package/dist/slices/shorts/prompts.js +4 -14
  508. package/dist/slices/shorts/render.js +6 -2
  509. package/dist/slices/social/channel.js +44 -0
  510. package/dist/slices/social/defaults.js +17 -0
  511. package/dist/slices/social/due.js +36 -0
  512. package/dist/slices/social/extension.js +18 -0
  513. package/dist/slices/social/fit.js +115 -0
  514. package/dist/slices/social/limits.js +186 -0
  515. package/dist/slices/social/links.js +21 -0
  516. package/dist/slices/social/media.js +132 -0
  517. package/dist/slices/social/model.js +75 -0
  518. package/dist/slices/social/notice.js +19 -0
  519. package/dist/slices/social/pack.js +176 -0
  520. package/dist/slices/social/post-prompt.js +56 -0
  521. package/dist/slices/social/queue.js +31 -0
  522. package/dist/slices/social/repo.js +133 -0
  523. package/dist/slices/social/reshape.js +97 -0
  524. package/dist/slices/social/rules.js +5 -0
  525. package/dist/slices/social/schema.js +70 -0
  526. package/dist/slices/social/serve.js +51 -0
  527. package/dist/slices/social/service.js +129 -0
  528. package/dist/slices/social/settings.js +21 -0
  529. package/dist/slices/social/sources.js +114 -0
  530. package/dist/slices/social/state.js +110 -0
  531. package/dist/slices/social/text.js +149 -0
  532. package/dist/slices/storage/arrange.js +3 -1
  533. package/dist/slices/storage/assets.js +8 -10
  534. package/dist/slices/storage/backup-designs.js +74 -0
  535. package/dist/slices/storage/backup-export.js +8 -3
  536. package/dist/slices/storage/backup-format.js +10 -0
  537. package/dist/slices/storage/backup-import.js +22 -4
  538. package/dist/slices/storage/delete-project.js +6 -5
  539. package/dist/slices/storage/files-location.js +6 -8
  540. package/dist/slices/storage/layout.js +13 -2
  541. package/dist/slices/storage/model.js +3 -0
  542. package/dist/slices/storage/portable.js +17 -12
  543. package/dist/slices/storage/schema.js +2 -0
  544. package/dist/slices/storage/staging-refs.js +2 -1
  545. package/dist/slices/storage/staging.js +6 -9
  546. package/dist/slices/storage/trim.js +26 -9
  547. package/dist/slices/studio/calendar.js +7 -2
  548. package/dist/slices/studio/disclosure.js +4 -4
  549. package/dist/slices/studio/model.js +1 -1
  550. package/dist/slices/studio/pack.js +9 -8
  551. package/dist/slices/studio/pick.js +2 -11
  552. package/dist/slices/studio/plan.js +7 -16
  553. package/dist/slices/studio/queue.js +3 -14
  554. package/dist/slices/studio/releases.js +26 -0
  555. package/dist/slices/studio/settings.js +20 -57
  556. package/dist/slices/studio/videos.js +35 -4
  557. package/dist/slices/style-preview/render.js +37 -16
  558. package/dist/slices/style-preview/schema.js +14 -1
  559. package/dist/slices/style-preview/service.js +2 -1
  560. package/dist/slices/style-preview/settings.js +33 -5
  561. package/dist/slices/subtitles/captions.js +1 -1
  562. package/dist/slices/telemetry/collector-client.js +2 -4
  563. package/dist/slices/telemetry/flush.js +2 -4
  564. package/dist/slices/telemetry/model.js +0 -7
  565. package/dist/slices/telemetry/record.js +10 -3
  566. package/dist/slices/template-studio/anatomy-view.js +161 -0
  567. package/dist/slices/template-studio/anatomy.js +73 -0
  568. package/dist/slices/template-studio/bakeoff-make.js +225 -0
  569. package/dist/slices/template-studio/bakeoff-repo.js +83 -0
  570. package/dist/slices/template-studio/bakeoff.js +333 -0
  571. package/dist/slices/template-studio/budget.js +106 -0
  572. package/dist/slices/template-studio/cards.js +78 -0
  573. package/dist/slices/template-studio/checks.js +322 -0
  574. package/dist/slices/template-studio/document-sample.js +56 -0
  575. package/dist/slices/template-studio/edit.js +15 -0
  576. package/dist/slices/template-studio/for-all.js +161 -0
  577. package/dist/slices/template-studio/free-video.js +31 -0
  578. package/dist/slices/template-studio/free.js +176 -0
  579. package/dist/slices/template-studio/gate.js +40 -0
  580. package/dist/slices/template-studio/hidden.js +20 -0
  581. package/dist/slices/template-studio/jobs.js +79 -0
  582. package/dist/slices/template-studio/loudness.js +111 -0
  583. package/dist/slices/template-studio/make.js +40 -0
  584. package/dist/slices/template-studio/model.js +58 -0
  585. package/dist/slices/template-studio/pack-import.js +253 -0
  586. package/dist/slices/template-studio/pack-samples.js +60 -0
  587. package/dist/slices/template-studio/pack.js +207 -0
  588. package/dist/slices/template-studio/paid-view.js +66 -0
  589. package/dist/slices/template-studio/paid.js +355 -0
  590. package/dist/slices/template-studio/quote.js +155 -0
  591. package/dist/slices/template-studio/ready.js +104 -0
  592. package/dist/slices/template-studio/repo.js +134 -0
  593. package/dist/slices/template-studio/reviews-path.js +58 -0
  594. package/dist/slices/template-studio/runner.js +84 -0
  595. package/dist/slices/template-studio/save.js +236 -0
  596. package/dist/slices/template-studio/say-it-as.js +248 -0
  597. package/dist/slices/template-studio/schedule-gate.js +34 -0
  598. package/dist/slices/template-studio/schedule-price.js +32 -0
  599. package/dist/slices/template-studio/schedule-sample.js +91 -0
  600. package/dist/slices/template-studio/schema.js +243 -0
  601. package/dist/slices/template-studio/scope.js +124 -0
  602. package/dist/slices/template-studio/sections.js +176 -0
  603. package/dist/slices/template-studio/service.js +175 -0
  604. package/dist/slices/template-studio/status.js +191 -0
  605. package/dist/slices/template-studio/topic.js +223 -0
  606. package/dist/slices/thumbnail/by-llm.js +1 -1
  607. package/dist/slices/trash/designs.js +76 -0
  608. package/dist/slices/trash/model.js +13 -4
  609. package/dist/slices/trash/service.js +52 -2
  610. package/dist/slices/tutorials/library.js +3 -2
  611. package/dist/slices/uploads/repo.js +37 -1
  612. package/dist/slices/uploads/rules.js +3 -2
  613. package/dist/slices/video/ass-text.js +17 -0
  614. package/dist/slices/video/audio-export.js +2 -64
  615. package/dist/slices/video/edit-list.js +2 -2
  616. package/dist/slices/video/edit-settings.js +1 -1
  617. package/dist/slices/video/encoding.js +169 -0
  618. package/dist/slices/video/ffmpeg.js +23 -21
  619. package/dist/slices/video/figure-card.js +1 -15
  620. package/dist/slices/video/plan.js +2 -1
  621. package/dist/slices/video/slideshow.js +43 -18
  622. package/dist/slices/voices/attribution.js +3 -2
  623. package/dist/slices/voices/model.js +9 -8
  624. package/dist/slices/voices/palette.js +0 -3
  625. package/dist/slices/voices/script.js +2 -1
  626. package/dist/slices/youtube/answer.js +4 -16
  627. package/dist/slices/youtube/edits-repo.js +3 -12
  628. package/dist/tutorials/AI-CLIs.md +25 -25
  629. package/dist/tutorials/Agents-MCP.md +34 -33
  630. package/dist/tutorials/Assistant.md +42 -0
  631. package/dist/tutorials/Backups.md +7 -7
  632. package/dist/tutorials/Calendar.md +17 -25
  633. package/dist/tutorials/Cast-Library.md +19 -19
  634. package/dist/tutorials/Channels.md +42 -35
  635. package/dist/tutorials/Costs-and-Run-Cost.md +38 -40
  636. package/dist/tutorials/Create-Article.md +114 -0
  637. package/dist/tutorials/Create-Extras.md +112 -0
  638. package/dist/tutorials/Create-Images.md +165 -0
  639. package/dist/tutorials/Create-Narration.md +178 -0
  640. package/dist/tutorials/Create-Overview.md +244 -0
  641. package/dist/tutorials/Create-Video-and-Style.md +144 -0
  642. package/dist/tutorials/Docker.md +3 -3
  643. package/dist/tutorials/Document-Themes.md +32 -32
  644. package/dist/tutorials/Editing-a-Project.md +94 -94
  645. package/dist/tutorials/FAQ.md +18 -18
  646. package/dist/tutorials/First-Launch-and-Welcome.md +50 -64
  647. package/dist/tutorials/Home-and-Projects.md +115 -71
  648. package/dist/tutorials/Home.md +33 -33
  649. package/dist/tutorials/Install.md +14 -13
  650. package/dist/tutorials/Intros-and-Outros.md +33 -34
  651. package/dist/tutorials/Keyboard-Shortcuts-and-Command-Palette.md +62 -58
  652. package/dist/tutorials/Keywords-Slopify-Fills.md +87 -0
  653. package/dist/tutorials/Models.md +53 -21
  654. package/dist/tutorials/Multiple-Voices.md +41 -40
  655. package/dist/tutorials/Narration-Aliases-and-Glossary.md +14 -14
  656. package/dist/tutorials/Notifications.md +27 -17
  657. package/dist/tutorials/Other-Languages.md +15 -15
  658. package/dist/tutorials/PDF-Documents.md +38 -29
  659. package/dist/tutorials/Posting-to-Social-Sites.md +121 -0
  660. package/dist/tutorials/Project-Page.md +118 -120
  661. package/dist/tutorials/Prompts-and-Templates.md +84 -0
  662. package/dist/tutorials/Prompts.md +48 -44
  663. package/dist/tutorials/Providers-and-Keys.md +105 -98
  664. package/dist/tutorials/Publishing-to-YouTube.md +31 -31
  665. package/dist/tutorials/Recovery-and-Retries.md +31 -31
  666. package/dist/tutorials/Remote-Access.md +63 -0
  667. package/dist/tutorials/Reviews-and-Checkpoints.md +98 -90
  668. package/dist/tutorials/Schedules.md +46 -27
  669. package/dist/tutorials/Settings-Reference.md +194 -174
  670. package/dist/tutorials/Shorts.md +34 -33
  671. package/dist/tutorials/Start-at-Login.md +3 -3
  672. package/dist/tutorials/Studio-Extension.md +22 -24
  673. package/dist/tutorials/Template-Studio.md +128 -0
  674. package/dist/tutorials/Templates.md +51 -77
  675. package/dist/tutorials/Trash.md +4 -4
  676. package/dist/tutorials/Troubleshooting.md +44 -44
  677. package/dist/tutorials/Uninstalling-and-Moving.md +5 -5
  678. package/dist/tutorials/Updating-and-Patch-Notes.md +18 -18
  679. package/dist/tutorials/Video-Editing.md +34 -34
  680. package/dist/tutorials/Where-Your-Files-Live.md +10 -10
  681. package/dist/tutorials/YouTube-Description.md +26 -26
  682. package/dist/tutorials/Your-First-Short.md +35 -35
  683. package/dist/tutorials/_Sidebar.md +19 -14
  684. package/dist/updater/work-in-progress.js +42 -12
  685. package/dist/web/assets/index-BNftKQrA.css +1 -0
  686. package/dist/web/assets/index-Cpa58woP.js +459 -0
  687. package/dist/web/assets/pdf-BKh8f7R6.js +56 -0
  688. package/dist/web/assets/pdf.worker.min-BmVo14Nb.mjs +28 -0
  689. package/dist/web/assets/pdf.worker.min-D7i8o3wR.js +1 -0
  690. package/dist/web/index.html +2 -2
  691. package/package.json +6 -2
  692. package/dist/slices/article/provided-entries.js +0 -66
  693. package/dist/slices/article/run.js +0 -145
  694. package/dist/slices/images/run.js +0 -234
  695. package/dist/slices/narration/legacy-plan.js +0 -58
  696. package/dist/slices/narration/run.js +0 -270
  697. package/dist/slices/rebuild/runtime-actions.js +0 -90
  698. package/dist/slices/research/run.js +0 -224
  699. package/dist/slices/subtitles/prepare.js +0 -194
  700. package/dist/slices/subtitles/transcript.js +0 -34
  701. package/dist/slices/thumbnail/run.js +0 -219
  702. package/dist/slices/video/audio-inputs.js +0 -33
  703. package/dist/slices/video/reuse-audio.js +0 -62
  704. package/dist/slices/video/run.js +0 -78
  705. package/dist/slices/video/write-export.js +0 -158
  706. package/dist/slices/video/write-subtitles.js +0 -59
  707. package/dist/tutorials/Library-Overview.md +0 -76
  708. package/dist/tutorials/Play-Images.md +0 -161
  709. package/dist/tutorials/Play-Narration.md +0 -167
  710. package/dist/tutorials/Play-Outputs.md +0 -109
  711. package/dist/tutorials/Play-Overview.md +0 -227
  712. package/dist/tutorials/Play-Title-and-Article.md +0 -136
  713. package/dist/tutorials/Play-Video-and-Style.md +0 -143
  714. package/dist/web/assets/index-BadsoORk.css +0 -1
  715. package/dist/web/assets/index-D8LqNsCl.js +0 -326
  716. package/dist/web/assets/pdf-T7MCwjIv.js +0 -54
  717. package/dist/web/assets/pdf.worker.min-Dswkl-cV.mjs +0 -29
  718. package/dist/web/assets/pdf.worker.min-zC6nfbxG.js +0 -1
@@ -0,0 +1,30 @@
1
+ import { statfs } from "node:fs/promises";
2
+ import { z } from "zod";
3
+ // The autopilot's hourly look at what may stop the schedules: the OpenRouter key's spending
4
+ // limit (`GET /api/v1/key`, the same read Settings → Keys & services' Test makes) and the free disk.
5
+ // Read-only: neither starts paid work.
6
+ const keyAnswer = z.object({
7
+ data: z.object({ limit: z.number().nullable(), limit_remaining: z.number().nullable() }).loose(),
8
+ });
9
+ // The key's limit and what is left of it; null for a key with no limit. Throws, with the
10
+ // status only, when OpenRouter refuses or answers something else: the caller skips the check.
11
+ export async function openRouterKeyLimit(fetchImpl, key, signal) {
12
+ const response = await fetchImpl("https://openrouter.ai/api/v1/key", {
13
+ headers: { Authorization: `Bearer ${key}` },
14
+ signal,
15
+ });
16
+ if (!response.ok)
17
+ throw new Error(`OpenRouter answered ${String(response.status)}`);
18
+ const parsed = keyAnswer.safeParse(await response.json());
19
+ if (!parsed.success)
20
+ throw new Error("OpenRouter's answer wasn't the key's limits");
21
+ const { limit, limit_remaining: remaining } = parsed.data.data;
22
+ return limit === null || remaining === null ? null : { limit, remaining };
23
+ }
24
+ export async function diskSpaceAt(path) {
25
+ const stats = await statfs(path);
26
+ return {
27
+ freeBytes: Number(stats.bavail) * Number(stats.bsize),
28
+ totalBytes: Number(stats.blocks) * Number(stats.bsize),
29
+ };
30
+ }
@@ -33,7 +33,7 @@ export async function decodeAudio(ffmpeg, source, output, signal) {
33
33
  child.on("close", (code) => {
34
34
  signal.removeEventListener("abort", abort);
35
35
  if (signal.aborted)
36
- reject(new Error("Subtitle audio preparation was canceled."));
36
+ reject(new Error("Caption audio preparation was canceled."));
37
37
  else if (code !== 0)
38
38
  reject(new Error(`Could not prepare the narration for subtitles. ${errorText}`));
39
39
  else
@@ -5,6 +5,7 @@ import { join } from "node:path";
5
5
  import { Readable } from "node:stream";
6
6
  import { pipeline } from "node:stream/promises";
7
7
  import { setTimeout as delay } from "node:timers/promises";
8
+ import { hasErrnoCode } from "../../kernel/errors.js";
8
9
  import { defaultEnglishSubtitleModel, } from "../../kernel/ports/subtitles.js";
9
10
  // Xenova/wav2vec2-base-960h at one pinned revision, which carries the same model at three
10
11
  // precisions. Each keeps its own file name so the cache can hold any of them at once; int8
@@ -144,7 +145,7 @@ async function downloadModel(input, deps, target) {
144
145
  catch (error) {
145
146
  if (await verified(target, deps.model, input.signal))
146
147
  return target;
147
- if (!isCode(error, "EEXIST") && !isCode(error, "EPERM"))
148
+ if (!hasErrnoCode(error, "EEXIST") && !hasErrnoCode(error, "EPERM"))
148
149
  throw error;
149
150
  await rm(target, { force: true });
150
151
  await rename(part, target);
@@ -169,7 +170,7 @@ async function seedModel(input, seed, target) {
169
170
  await rm(staging, { recursive: true, force: true });
170
171
  }
171
172
  }
172
- const smaller = " To download less, pick a smaller model in Settings → Production defaults → Subtitle timing model.";
173
+ const smaller = " To download less, pick a smaller model in Settings → Production defaults → Caption timing model.";
173
174
  function megabytes(bytes) {
174
175
  return `${String(Math.round(bytes / 1_000_000))} MB`;
175
176
  }
@@ -187,11 +188,8 @@ async function verified(path, model, signal) {
187
188
  return hash.digest("hex") === model.sha256;
188
189
  }
189
190
  catch (error) {
190
- if (isCode(error, "ENOENT"))
191
+ if (hasErrnoCode(error, "ENOENT"))
191
192
  return false;
192
193
  throw error;
193
194
  }
194
195
  }
195
- function isCode(error, code) {
196
- return error instanceof Error && "code" in error && error.code === code;
197
- }
@@ -1,6 +1,5 @@
1
- import { englishMismatch, englishSpec } from "./spec.js";
1
+ import { englishSpec } from "./spec.js";
2
2
  export const frameSeconds = 0.02;
3
- export const mismatch = englishMismatch;
4
3
  export function alignWindow(logits, frames, words, complete, spec = englishSpec) {
5
4
  const labels = spec.labels;
6
5
  if (frames < 1 || frames > 1100 || logits.length !== frames * labels)
@@ -1,6 +1,7 @@
1
1
  import { open, readFile, rm, stat } from "node:fs/promises";
2
2
  import { join } from "node:path";
3
3
  import { setTimeout } from "node:timers/promises";
4
+ import { hasErrnoCode } from "../../kernel/errors.js";
4
5
  // Models run one at a time per data directory, including requests from separate app
5
6
  // processes. A canceled waiter never removes another request's lock.
6
7
  export async function claimWorker(cacheDir, signal) {
@@ -26,7 +27,7 @@ export async function claimWorker(cacheDir, signal) {
26
27
  };
27
28
  }
28
29
  catch (error) {
29
- if (!(error instanceof Error && "code" in error && error.code === "EEXIST"))
30
+ if (!hasErrnoCode(error, "EEXIST"))
30
31
  throw error;
31
32
  }
32
33
  try {
@@ -41,7 +42,7 @@ export async function claimWorker(cacheDir, signal) {
41
42
  process.kill(owner, 0);
42
43
  }
43
44
  catch (error) {
44
- if (error instanceof Error && "code" in error && error.code === "ESRCH") {
45
+ if (hasErrnoCode(error, "ESRCH")) {
45
46
  await rm(path, { force: true });
46
47
  continue;
47
48
  }
@@ -50,7 +51,7 @@ export async function claimWorker(cacheDir, signal) {
50
51
  }
51
52
  }
52
53
  catch (error) {
53
- if (!(error instanceof Error && "code" in error && error.code === "ENOENT"))
54
+ if (!hasErrnoCode(error, "ENOENT"))
54
55
  throw error;
55
56
  }
56
57
  await setTimeout(250, undefined, { signal });
@@ -1,4 +1,5 @@
1
1
  import { languageInfo } from "../../kernel/ports/languages.js";
2
+ import { editSettings } from "../../kernel/ports/settings-places.js";
2
3
  import { englishSpec } from "./spec.js";
3
4
  import { spellers, spokenNumber } from "./spell.js";
4
5
  import { respoken, speechWords } from "./text.js";
@@ -121,9 +122,6 @@ export const multilingualGates = {
121
122
  anchorLetters: 20,
122
123
  anchorConfidence: 0.75,
123
124
  };
124
- export function multilingualLanguages() {
125
- return Object.keys(alphabets);
126
- }
127
125
  // The model and language view the aligner uses for a project language. English keeps its own
128
126
  // model and every constant it had.
129
127
  export function alignmentSpecFor(language) {
@@ -242,7 +240,7 @@ function spell(text, alphabet, raw, language) {
242
240
  }
243
241
  const folded = fold(character);
244
242
  if (folded === undefined)
245
- throw new Error(`Word timing for ${languageInfo(language).name} reads Latin letters only, and "${raw}" has letters from another alphabet. Write that word in Latin letters in the article (open the project, Edit project → Article), then Try again.`);
243
+ throw new Error(`Word timing for ${languageInfo(language).name} reads Latin letters only, and "${raw}" has letters from another alphabet. Write that word in Latin letters in the article (${editSettings.article}), then Try again.`);
246
244
  result += folded;
247
245
  }
248
246
  return result.replace(/\s+/g, " ").trim();
@@ -1,5 +1,6 @@
1
1
  import { mkdir, readFile, rm } from "node:fs/promises";
2
2
  import { join } from "node:path";
3
+ import { hasErrnoCode } from "../../kernel/errors.js";
3
4
  import { defaultEnglishSubtitleModel, } from "../../kernel/ports/subtitles.js";
4
5
  import { englishModels, prepareModel } from "./cache.js";
5
6
  import { claimWorker } from "./lock.js";
@@ -31,7 +32,7 @@ async function clearOwnLock(cacheDir) {
31
32
  owner = await readFile(path, "utf8");
32
33
  }
33
34
  catch (error) {
34
- if (error instanceof Error && "code" in error && error.code === "ENOENT")
35
+ if (hasErrnoCode(error, "ENOENT"))
35
36
  return;
36
37
  throw error;
37
38
  }
@@ -24,7 +24,7 @@ export async function runAlignmentWorker(input, signal, onProgress, worker = new
24
24
  signal.removeEventListener("abort", abort);
25
25
  child.kill("SIGKILL");
26
26
  };
27
- const abort = () => finish(new Error("Subtitle alignment was canceled."));
27
+ const abort = () => finish(new Error("Caption timing was canceled."));
28
28
  signal.addEventListener("abort", abort, { once: true });
29
29
  child.stderr?.on("data", (data) => {
30
30
  stderr = (stderr + data.toString("utf8")).slice(-2000);
@@ -1,4 +1,5 @@
1
1
  import { open, readFile, stat } from "node:fs/promises";
2
+ import { errorText } from "../../kernel/errors.js";
2
3
  import { SubtitleMismatch } from "../../kernel/ports/subtitles.js";
3
4
  import { alignmentSpecFor } from "./multilingual.js";
4
5
  import { workerInput } from "./protocol.js";
@@ -15,7 +16,7 @@ process.once("message", (raw) => {
15
16
  }
16
17
  run(input.data).then((words) => send({ type: "done", words }), (error) => send({
17
18
  type: "error",
18
- message: error instanceof Error ? error.message : String(error),
19
+ message: errorText(error),
19
20
  ...(error instanceof SubtitleMismatch
20
21
  ? { mismatch: { at: error.at, expected: error.expected, heard: error.heard } }
21
22
  : {}),
@@ -167,7 +168,7 @@ async function openSession(modelPath) {
167
168
  const reason = nativeError instanceof Error
168
169
  ? (nativeError.message.split("\n")[0] ?? "")
169
170
  : String(nativeError);
170
- throw new Error(`Subtitle timing could not start: neither of its speech engines loaded on this computer (${system}): ${reason}. Reinstall Slopify so npm installs the engine for this system, then try again; if it keeps failing, turn subtitles off for this project.`);
171
+ throw new Error(`Caption timing could not start: neither of its speech engines loaded on this computer (${system}): ${reason}. Reinstall Slopify so npm installs the engine for this system, then try again; if it keeps failing, turn captions off for this project.`);
171
172
  }
172
173
  }
173
174
  const session = await native.InferenceSession.create(modelPath, {
@@ -1,3 +1,4 @@
1
+ import { editSettings } from "../kernel/ports/settings-places.js";
1
2
  // The sentences a failed provider call shows. Whatever an adapter throws becomes the stage's
2
3
  // failure reason on the project page, read by someone who only wants the video, so each one
3
4
  // says what failed, the likely cause and the next step, using the names on screen. The
@@ -6,19 +7,19 @@
6
7
  // A provider's error body can be a whole HTML page; the first few hundred characters say why.
7
8
  const detailMax = 300;
8
9
  export function missingKey(provider) {
9
- return `No ${provider} API key is saved. Add one in Settings → Providers, then use Try again.`;
10
+ return `No ${provider} API key is saved. Add one in Settings → Keys & services, then use Try again.`;
10
11
  }
11
12
  export const refreshList = "refresh the list";
12
- export const checkChoice = "Check the provider and model chosen in the Providers section of Edit project, then use Try again.";
13
+ export const checkChoice = `Check the service and model chosen in ${editSettings.open} (under Written by in Article, in Narration or in Images), then use Try again.`;
13
14
  export function httpFailure(failure) {
14
15
  const { provider, status } = failure;
15
16
  const said = quoted(failure.label ?? `error ${String(status)}`, failure.detail);
16
17
  const next = failure.next ?? "use Try again";
17
18
  if (status === 401) {
18
- return `${provider} did not accept the API key${said}. The key may be wrong, expired or revoked: paste a current key in Settings → Providers, then ${next}.`;
19
+ return `${provider} did not accept the API key${said}. The key may be wrong, expired or revoked: paste a current key in Settings → Keys & services, then ${next}.`;
19
20
  }
20
21
  if (status === 403) {
21
- return `${provider} refused access${said}. The API key may not be allowed to use this model, or the account needs attention: check your ${provider} account and the key in Settings → Providers, then ${next}.`;
22
+ return `${provider} refused access${said}. The API key may not be allowed to use this model, or the account needs attention: check your ${provider} account and the key in Settings → Keys & services, then ${next}.`;
22
23
  }
23
24
  if (status === 402) {
24
25
  return `${provider} says the account is out of credits${said}. Add credits on the ${provider} website, then ${next}.`;
@@ -27,7 +28,7 @@ export function httpFailure(failure) {
27
28
  return `${provider} is limiting requests${said}. Too many requests were sent at once or the plan's quota is used up: wait a few minutes or check your ${provider} usage and billing, then ${next}.`;
28
29
  }
29
30
  if (status >= 400 && status < 500) {
30
- return `${provider} rejected the ${failure.subject ?? "request"}${said}. ${failure.fix ?? (failure.next === undefined ? checkChoice : `Check the key in Settings → Providers, then ${next}.`)}`;
31
+ return `${provider} rejected the ${failure.subject ?? "request"}${said}. ${failure.fix ?? (failure.next === undefined ? checkChoice : `Check the key in Settings → Keys & services, then ${next}.`)}`;
31
32
  }
32
33
  if (status >= 500) {
33
34
  return `${provider} had a problem on its side${said}. This is usually temporary: wait a few minutes, then ${next}.`;
@@ -48,18 +49,15 @@ export function noAudio(provider) {
48
49
  return `${provider} finished without sending any audio. Use Try again; if it keeps happening, check the voice in Settings → Voices.`;
49
50
  }
50
51
  export function noImage(provider) {
51
- return `${provider} finished without sending an image. Use Try again; if it keeps happening, reword the image prompt in the Images section of Edit project or choose another image model in its Providers section.`;
52
+ return `${provider} finished without sending an image. Use Try again; if it keeps happening, reword the image prompt in ${editSettings.images} or choose another image model there.`;
52
53
  }
53
54
  export function refusedImage(provider, detail) {
54
- return `${provider} refused to make this image under its content rules${quoted("", detail)}. Reword the image prompt in the Images section of Edit project, or choose another image provider in its Providers section.`;
55
+ return `${provider} refused to make this image under its content rules${quoted("", detail)}. Reword the image prompt in ${editSettings.images}, or choose another image service there.`;
55
56
  }
56
57
  // A narration request the provider turned down is most often a voice that no longer exists
57
58
  // or a part longer than the model takes.
58
59
  export function voiceFix(voiceId) {
59
- return `Check that voice "${voiceId}" still exists in Settings → Voices and that each narration part fits the model's length limit (Chunking, in the Providers section of Edit project), then use Try again.`;
60
- }
61
- export function internalError(what) {
62
- return `Slopify hit an internal error (${what}). Use Try again; if it happens again, use Download diagnostics in Settings and report it.`;
60
+ return `Check that voice "${voiceId}" still exists in Settings → Voices and that each narration part fits the model's length limit (Chunking, in ${editSettings.narration}), then use Try again.`;
63
61
  }
64
62
  const cliNames = {
65
63
  claude: "Claude Code CLI",
@@ -82,7 +80,7 @@ export function cliReported(binary, detail, next) {
82
80
  return `The ${cliName(binary)} reported an error${quoted("", detail)}. ${next}`;
83
81
  }
84
82
  export function cliCheck(binary) {
85
- return `Use Try again; if it keeps failing, run ${commandOf(binary)} in a terminal to check it works and is signed in, or choose another model in the Providers section of Edit project.`;
83
+ return `Use Try again; if it keeps failing, run ${commandOf(binary)} in a terminal to check it works and is signed in, or choose another model in ${editSettings.writer}.`;
86
84
  }
87
85
  // ` (error 401: "Unauthorized")`, ` (error 401)`, ` ("Unauthorized")`, or nothing.
88
86
  export function quoted(label, detail) {
@@ -4,6 +4,7 @@ import { createRequire } from "node:module";
4
4
  import { join } from "node:path";
5
5
  import { promisify } from "node:util";
6
6
  import { z } from "zod";
7
+ import { errorText } from "../kernel/errors.js";
7
8
  import { redact } from "../kernel/log.js";
8
9
  // ffmpeg-static exports a path even when npm skipped its install script. Recover using
9
10
  // that dependency's own downloader, including its source notice and license, without
@@ -73,6 +74,23 @@ async function verify(bin) {
73
74
  throw new Error(`ffmpeg at ${bin} could not be started. Reinstall ffmpeg or check your antivirus has not quarantined it, then restart Slopify. ${remedy} ${detail(error)}`);
74
75
  }
75
76
  }
77
+ // One short ffmpeg run to its end, answering its stdout; a failure carries the last thing it
78
+ // said on stderr. For questions about an ffmpeg (its encoders, a test encode), never renders.
79
+ export async function ffmpegOutput(bin, args) {
80
+ try {
81
+ const { stdout } = await promisify(execFile)(bin, [...args], {
82
+ windowsHide: true,
83
+ timeout: 30_000,
84
+ maxBuffer: 4 * 1024 * 1024,
85
+ });
86
+ return stdout;
87
+ }
88
+ catch (error) {
89
+ const stderr = z.object({ stderr: z.string() }).safeParse(error);
90
+ const said = stderr.success ? stderr.data.stderr.trim().split("\n").slice(-2).join(" / ") : "";
91
+ throw new Error(said === "" ? detail(error) : redact(said));
92
+ }
93
+ }
76
94
  function detail(error) {
77
- return redact(error instanceof Error ? error.message : String(error));
95
+ return redact(errorText(error));
78
96
  }
@@ -1,12 +1,14 @@
1
1
  import { redact } from "../../kernel/log.js";
2
2
  import { bridgeLimits, decodeHostImageReport, hostFaultSchema, hostFrameSchema, hostHealthSchema, hostImageReportHeader, hostImageSchema, hostLlmSchema, hostModelsSchema, hostOpenedSchema, hostOpenFolderSchema, hostStatusSchema, } from "../../kernel/ports/host-cli.js";
3
3
  import { agentImageTimeoutMs } from "../../kernel/ports/image.js";
4
- import { isProviderError, providerError } from "../../kernel/ports/model.js";
4
+ import { isProviderError, providerError, } from "../../kernel/ports/model.js";
5
+ import { editSettings } from "../../kernel/ports/settings-places.js";
5
6
  import { sniffImage } from "../image/bytes.js";
6
7
  import { claudePlanLimit } from "../llm/claude-code.js";
7
8
  import { codexPlanLimit } from "../llm/codex-limits.js";
9
+ import { accessField } from "../model-access.js";
8
10
  import { hostRequest, hostUnavailable, readHostBytes, } from "./transport.js";
9
- const tooBigForHost = "Slopify could not send this request to the host helper because it is larger than, or shaped differently from, what the helper accepts. Make the inputs shorter in Edit project, then use Try again; if it happens again, use Download diagnostics in Settings and report it.";
11
+ const tooBigForHost = `Slopify could not send this request to the host helper because it is larger than, or shaped differently from, what the helper accepts. Make the article or its research notes shorter in ${editSettings.article}, then use Try again; if it happens again, use Download diagnostics in Settings and report it.`;
10
12
  export function createHostCliClient(options) {
11
13
  async function request(input) {
12
14
  if (!options.directory)
@@ -16,7 +18,7 @@ export function createHostCliClient(options) {
16
18
  try {
17
19
  const parsed = hostFaultSchema.safeParse(JSON.parse((await readHostBytes(response, bridgeLimits.status)).toString("utf8")));
18
20
  if (parsed.success)
19
- throw providerError({ kind: parsed.data.kind, message: redact(parsed.data.message) });
21
+ throw bridgedFault(parsed.data.kind, parsed.data.message);
20
22
  }
21
23
  catch (error) {
22
24
  if (isProviderError(error))
@@ -163,6 +165,16 @@ export function createHostCliClient(options) {
163
165
  base64: Buffer.from(req.reference.bytes).toString("base64"),
164
166
  },
165
167
  }),
168
+ ...(req.edit === undefined
169
+ ? {}
170
+ : {
171
+ edit: {
172
+ source: {
173
+ mime: req.edit.source.mime,
174
+ base64: Buffer.from(req.edit.source.bytes).toString("base64"),
175
+ },
176
+ },
177
+ }),
166
178
  ...(req.cast === undefined
167
179
  ? {}
168
180
  : {
@@ -216,6 +228,11 @@ export function createHostCliClient(options) {
216
228
  },
217
229
  };
218
230
  }
231
+ // A fault from the host helper carries its kind and the host adapter's words only; whether it
232
+ // was about the model's access is read again from those words, as the plan limit is below.
233
+ export function bridgedFault(kind, message) {
234
+ return providerError({ kind, message: redact(message), ...accessField(null, message) });
235
+ }
219
236
  // A used-up plan crosses the bridge as a rate-limit fault carrying the CLI's own words, which
220
237
  // the host-side adapter already quoted; they are read again here so a Docker run waits for the
221
238
  // reset like a local one instead of failing.
@@ -256,7 +273,7 @@ async function* readEvents(response) {
256
273
  throw hostUnavailable(true);
257
274
  const event = hostFrameSchema.parse(JSON.parse(line));
258
275
  if (event.type === "error")
259
- throw providerError({ kind: event.kind, message: redact(event.message) });
276
+ throw bridgedFault(event.kind, event.message);
260
277
  if (event.type === "done")
261
278
  terminal = event;
262
279
  else
@@ -5,6 +5,7 @@ import { join } from "node:path";
5
5
  import { bridgeLimits, hostFramesHeader, hostFramesVersion } from "../../kernel/ports/host-cli.js";
6
6
  import { agentImageTimeoutMs } from "../../kernel/ports/image.js";
7
7
  import { providerError } from "../../kernel/ports/model.js";
8
+ import { editSettings } from "../../kernel/ports/settings-places.js";
8
9
  export function hostUnavailable(submitted = false) {
9
10
  return providerError({
10
11
  kind: "unavailable",
@@ -34,7 +35,7 @@ export async function hostRequest(options) {
34
35
  if (options.body && options.body.byteLength > bridgeLimits.request)
35
36
  throw providerError({
36
37
  kind: "unsupported",
37
- message: "This request is larger than the host helper accepts (16 MB), so nothing was sent. Make the inputs shorter in Edit project, or choose an API provider such as OpenRouter in its Providers section, then use Try again.",
38
+ message: `This request is larger than the host helper accepts (16 MB), so nothing was sent. Make the article or its research notes shorter in ${editSettings.article}, or choose an API service such as OpenRouter under Written by there, then use Try again.`,
38
39
  });
39
40
  let token;
40
41
  try {
@@ -0,0 +1,53 @@
1
+ import { providerError } from "../kernel/ports/model.js";
2
+ import { missingKey } from "./explain.js";
3
+ import { retryAfter } from "./retry-after.js";
4
+ // The plumbing every HTTP provider adapter repeats around a failed call. What the provider
5
+ // said and which sentence it earns stay in each adapter; only the shape is shared.
6
+ export function safeJson(text) {
7
+ try {
8
+ return JSON.parse(text);
9
+ }
10
+ catch {
11
+ return undefined;
12
+ }
13
+ }
14
+ // The body of a failed response, or nothing when it cannot be read.
15
+ export async function failureText(response) {
16
+ return await response.text().catch(() => "");
17
+ }
18
+ // Only the adapter sees the vendor's status code, so only the adapter names the kind.
19
+ export function kindOfStatus(status) {
20
+ if (status === 401 || status === 403) {
21
+ return "auth";
22
+ }
23
+ if (status === 429) {
24
+ return "rate_limit";
25
+ }
26
+ // ceiling: everything else is `other` and is retried, so a request the provider will never
27
+ // accept fails the same way four times over. A terminal "this will never work" kind has to
28
+ // reach the port's error contract first, which is not an adapter's to widen.
29
+ // The provider's own server failed; the same request may well succeed later.
30
+ if (status >= 500)
31
+ return "dropped";
32
+ return "other";
33
+ }
34
+ // `missing_key`, not `auth`, because that rule makes it terminal: an absent key never becomes
35
+ // a request.
36
+ export function requireKey(key, provider) {
37
+ if (key === undefined || key === "") {
38
+ throw providerError({ kind: "missing_key", message: missingKey(provider) });
39
+ }
40
+ return key;
41
+ }
42
+ // A 429's Retry-After replaces the fixed backoff; `fallbackRetryMs` is the provider's own
43
+ // hint, used when the header is absent. `access` is what the failure says about the chosen
44
+ // model (see model-access.ts); it never changes the kind.
45
+ export function httpProviderError(response, kind, message, fallbackRetryMs, access) {
46
+ const retryAfterMs = retryAfter(response.headers.get("retry-after")) ?? fallbackRetryMs;
47
+ return providerError({
48
+ kind,
49
+ message,
50
+ ...(access === undefined ? {} : { access }),
51
+ ...(retryAfterMs === undefined ? {} : { retryAfterMs }),
52
+ });
53
+ }
@@ -1,4 +1,5 @@
1
1
  import { providerError } from "../../kernel/ports/model.js";
2
+ import { editSettings } from "../../kernel/ports/settings-places.js";
2
3
  // Two of the three image providers answer with a link rather than with bytes, so fetching
3
4
  // that link is part of the provider call and belongs inside the attempt with it: a URL
4
5
  // that 404s or hands back an HTML error page is a failed attempt, not a corrupt file on
@@ -48,7 +49,7 @@ export async function downloadImage(download) {
48
49
  if (mime === undefined) {
49
50
  throw providerError({
50
51
  kind: "other",
51
- message: `${download.provider} sent back something that is not a PNG or JPEG image (${describeBytes(bytes)}). Use Try again; if it keeps happening, choose another image model in the Providers section of Edit project.`,
52
+ message: `${download.provider} sent back something that is not a PNG or JPEG image (${describeBytes(bytes)}). Use Try again; if it keeps happening, choose another image model in ${editSettings.images}.`,
52
53
  });
53
54
  }
54
55
  return { bytes, mime };
@@ -5,14 +5,16 @@ import { z } from "zod";
5
5
  import { redact } from "../../kernel/log.js";
6
6
  import { agentImageTimeoutMs, } from "../../kernel/ports/image.js";
7
7
  import { providerError } from "../../kernel/ports/model.js";
8
+ import { editSettings } from "../../kernel/ports/settings-places.js";
8
9
  import { cliReported, quoted, refusedImage } from "../explain.js";
9
10
  import { cliLoginError } from "../llm/cli-login-error.js";
10
11
  import { codexLimitError, readingOf } from "../llm/codex.js";
11
12
  import { codexPlanLimit } from "../llm/codex-limits.js";
12
13
  import { cliEvent, cliShaped, endedKind, endedWithout, stopCliRun, } from "../llm/run-cli.js";
13
14
  import { lines } from "../llm/sse-lines.js";
15
+ import { accessField } from "../model-access.js";
14
16
  import { codexGeneratedImage, codexImageCount } from "./codex-output.js";
15
- import { referencePictures } from "./reference.js";
17
+ import { attachedPictures, editNote } from "./reference.js";
16
18
  // "Codex default" puts no model and no effort on the command line, so the CLI's own defaults
17
19
  // draw the image; it is what every project saved before the choice existed runs. Every other
18
20
  // model id is one of the Codex CLI's own models - the list the Codex text provider shows -
@@ -46,7 +48,7 @@ const alwaysDisabled = [
46
48
  function reviews(req) {
47
49
  return (req.model !== codexImageModel.id ||
48
50
  req.thinking !== undefined ||
49
- referencePictures(req).length > 0);
51
+ attachedPictures(req).length > 0);
50
52
  }
51
53
  // The binary's image tool (`ImagegenArgs`: prompt, referenced_image_paths,
52
54
  // num_last_images_to_include) takes each referenced image as an absolute path it reads
@@ -57,10 +59,12 @@ export function codexReferencePath(directory, image) {
57
59
  // Every picture the request carries, each at its own path: the establishing image keeps the
58
60
  // name it always had, the cast pictures follow it as reference-2, reference-3 and so on.
59
61
  export function codexReferencePaths(directory, req) {
60
- return referencePictures(req).map((picture, index) => ({
62
+ return attachedPictures(req).map((picture, index) => ({
61
63
  picture,
62
64
  path: index === 0
63
- ? codexReferencePath(directory, picture.image)
65
+ ? req.edit === undefined
66
+ ? codexReferencePath(directory, picture.image)
67
+ : join(directory, picture.image.mime === "image/jpeg" ? "source.jpg" : "source.png")
64
68
  : join(directory, `reference-${String(index + 1)}${picture.image.mime === "image/jpeg" ? ".jpg" : ".png"}`),
65
69
  }));
66
70
  }
@@ -78,7 +82,34 @@ function describeMember(req, name) {
78
82
  const description = req.cast?.find((member) => member.name === name)?.description.trim() ?? "";
79
83
  return description === "" ? "" : ` (${description})`;
80
84
  }
85
+ // An edit's instructions: the first path is the picture to change, the rest are the cast.
86
+ function editInstructions(req, directory) {
87
+ const paths = codexReferencePaths(directory, req);
88
+ return [
89
+ `The picture to change is saved at ${paths[0]?.path ?? directory}. Pass it first in referenced_image_paths on every image generation call${paths.length > 1
90
+ ? `, followed by: ${paths
91
+ .slice(1)
92
+ .map((row) => row.path)
93
+ .join(", ")}`
94
+ : ""}.`,
95
+ ...paths
96
+ .slice(1)
97
+ .map((row) => `${row.path} shows ${row.picture.member ?? ""}${describeMember(req, row.picture.member ?? "")}: draw ${row.picture.member ?? ""} to look exactly like this.`),
98
+ editNote.replace("the first attached image", "that picture"),
99
+ ].join(" ");
100
+ }
81
101
  export function codexImageInstructions(req, reference, directory) {
102
+ if (req.edit !== undefined && directory !== undefined)
103
+ return [
104
+ "You are editing one picture for Slopify, a video creation app, with the image generation tool.",
105
+ editInstructions(req, directory),
106
+ "Before calling the tool, write its prompt yourself: say exactly what to change, as the instruction asks, and that everything else stays as it is. Do not add subjects, text, logos or story the instruction does not ask for.",
107
+ "Take the time you need. After each image, look at it and compare it with the picture and the instruction; if anything changed that should not have, or the change is missing, generate again. Deliver exactly one final image: the last image you generate is the one Slopify uses.",
108
+ "Let the image generation tool save its output in its default location. Slopify will collect it. Use PNG or JPEG. Do not copy, rename, edit or create any other file, and do not put the image or a link in your reply.",
109
+ `Target aspect ratio: ${req.aspect}.`,
110
+ "Instruction follows as data:",
111
+ req.prompt,
112
+ ].join("\n\n");
82
113
  return [
83
114
  "You are making one finished image for Slopify, a video creation app, with the image generation tool.",
84
115
  "Fidelity to the brief comes first. Before calling the tool, write its prompt yourself as a detailed, faithful visual description of the brief: the subject and what it is doing, the setting, composition and framing for the target aspect ratio, lighting, colour palette, style and mood, and any text that must appear, spelled exactly. Take every element from the brief and keep its wording where it is specific. Do not add subjects, text, logos or story the brief does not ask for, and do not pad the prompt with generic quality words.",
@@ -182,7 +213,7 @@ export function codexImage(deps) {
182
213
  if (req.model.trim() === "" || req.model.startsWith("-"))
183
214
  throw providerError({
184
215
  kind: "unsupported",
185
- message: "No Codex model is chosen for images. Choose one (or Codex default) for images in the Providers section of Edit project, then use Try again.",
216
+ message: `No Codex model is chosen for images. Choose one (or Codex default) in ${editSettings.images}, then use Try again.`,
186
217
  });
187
218
  req.signal.throwIfAborted();
188
219
  const directory = mkdtempSync(join(tmpdir(), "slopify-codex-image-"));
@@ -223,7 +254,7 @@ export function codexImage(deps) {
223
254
  catch {
224
255
  throw providerError({
225
256
  kind: "unsupported",
226
- message: "The Codex CLI could not be started. Check it is installed and set up in Settings → Providers, then use Try again.",
257
+ message: "The Codex CLI could not be started. Check it is installed and set up in Settings → Keys & services, then use Try again.",
227
258
  });
228
259
  }
229
260
  let completed = false;
@@ -271,6 +302,7 @@ export function codexImage(deps) {
271
302
  message: /refus|content.policy|safety/i.test(message)
272
303
  ? refusedImage("The Codex CLI", redact(message))
273
304
  : cliReported(binary, redact(message), imageNext),
305
+ ...(/refus|content.policy|safety/i.test(message) ? {} : accessField(null, message)),
274
306
  });
275
307
  }
276
308
  else if (event.type === "error") {
@@ -288,6 +320,7 @@ export function codexImage(deps) {
288
320
  message: /image.generation|image tool|feature.*unavailable/i.test(message)
289
321
  ? `The Codex CLI cannot make images${quoted("", redact(message))}. ${cannotDraw}`
290
322
  : cliReported(binary, redact(message), imageNext),
323
+ ...accessField(null, message),
291
324
  });
292
325
  }
293
326
  else if (event.type === "item.completed") {
@@ -305,7 +338,7 @@ export function codexImage(deps) {
305
338
  if (ended.error !== null)
306
339
  throw providerError({
307
340
  kind: "unsupported",
308
- message: "The Codex CLI could not be started. Check it is installed and set up in Settings → Providers, then use Try again.",
341
+ message: "The Codex CLI could not be started. Check it is installed and set up in Settings → Keys & services, then use Try again.",
309
342
  });
310
343
  const stderrLimit = codexPlanLimit(run.stderr(), new Date());
311
344
  if ((ended.code !== 0 || !completed) && stderrLimit !== undefined)
@@ -365,8 +398,8 @@ async function* bounded(source, maximum) {
365
398
  yield chunk;
366
399
  }
367
400
  }
368
- const imageNext = "Use Try again; if it keeps failing, run codex in a terminal to check it works and is signed in, or choose another image provider in the Providers section of Edit project.";
369
- const cannotDraw = "Update the Codex CLI and check your ChatGPT plan includes image generation, or choose another image provider in the Providers section of Edit project.";
401
+ const imageNext = `Use Try again; if it keeps failing, run codex in a terminal to check it works and is signed in, or choose another image service in ${editSettings.images}.`;
402
+ const cannotDraw = `Update the Codex CLI and check your ChatGPT plan includes image generation, or choose another image service in ${editSettings.images}.`;
370
403
  function imageEnvironment(directory, source) {
371
404
  const env = { PWD: directory };
372
405
  const allowed = [