@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,71 @@
1
+ import { problem } from "./problem.js";
2
+ import { viaProxy } from "./same-page.js";
3
+ const id = "[^/]+";
4
+ export const hostOnlyWrites = [
5
+ {
6
+ method: "PUT|DELETE",
7
+ path: new RegExp(`^/api/providers/${id}/key/?$`),
8
+ what: "Provider keys are saved and removed (Settings → Keys & services)",
9
+ },
10
+ {
11
+ method: "PUT",
12
+ path: new RegExp(`^/api/providers/${id}/path/?$`),
13
+ what: "A CLI's path is set (Settings → Keys & services)",
14
+ },
15
+ {
16
+ method: "PUT",
17
+ path: /^\/api\/storage\/import\/?$/,
18
+ what: "A backup is imported (Settings → Backup & storage)",
19
+ },
20
+ {
21
+ method: "POST",
22
+ path: new RegExp(`^/api/storage/(?:cleanup|old-versions/delete|projects/${id}/(?:old-versions/delete|keep-outputs))/?$`),
23
+ what: "Clean-ups that delete files are run (Settings → Backup & storage)",
24
+ },
25
+ {
26
+ method: "POST",
27
+ path: /^\/api\/storage\/files\/move\/?$/,
28
+ what: "The Files folder is moved (Settings → Backup & storage)",
29
+ },
30
+ {
31
+ method: "PUT",
32
+ path: /^\/api\/backups\/?$/,
33
+ what: "The backup folder is set (Settings → Backup & storage)",
34
+ },
35
+ {
36
+ method: "POST|DELETE",
37
+ path: /^\/api\/update(?:\/activate)?\/?$/,
38
+ what: "Updates are installed (Settings → About)",
39
+ },
40
+ {
41
+ method: "PUT",
42
+ path: /^\/api\/settings\/autostart\/?$/,
43
+ what: "Start with this computer is turned on or off (Settings → General)",
44
+ },
45
+ ];
46
+ export const otherAccount = "Slopify refused this request: over remote access it answers only the Tailscale account that turned remote access on. Sign in to Tailscale on this device with that account, or open Slopify on the computer it runs on.";
47
+ export const hostOnlySentence = (what) => `${what} only on the computer Slopify runs on, not over remote access: open Slopify there, at http://127.0.0.1 and Slopify's port, and do it there.`;
48
+ export function remoteGuard(remoteAccess) {
49
+ return async (c, next) => {
50
+ const headers = c.req.raw.headers;
51
+ // The agents' endpoints refuse every proxied request themselves, with their own sentence.
52
+ if (!viaProxy(headers) || /^\/mcp(?:\/|$)/.test(c.req.path))
53
+ return next();
54
+ const owner = remoteAccess?.owner();
55
+ const remote = remoteAccess?.remoteHost();
56
+ const throughServe = headers.has("tailscale-user-login") ||
57
+ (remote !== undefined &&
58
+ (headers.get("x-forwarded-host") === remote || headers.get("host") === remote));
59
+ if (owner !== undefined && throughServe && headers.get("tailscale-user-login") !== owner)
60
+ return problem(c, { status: 403, detail: otherAccount });
61
+ const path = c.req.path;
62
+ const write = hostOnlyWrites.find((rule) => rule.method.split("|").includes(c.req.method) && rule.path.test(path));
63
+ if (write !== undefined)
64
+ return problem(c, {
65
+ status: 403,
66
+ detail: hostOnlySentence(write.what),
67
+ extensions: { reason: "host-only" },
68
+ });
69
+ return next();
70
+ };
71
+ }
@@ -0,0 +1,91 @@
1
+ import { isIP } from "node:net";
2
+ import { hostname } from "node:os";
3
+ import { actorOf } from "./actor.js";
4
+ import { problem } from "./problem.js";
5
+ import { forwardedHost, fromSlopifyPage } from "./same-page.js";
6
+ const nameOf = (host) => {
7
+ const trimmed = host.trim().toLowerCase();
8
+ if (trimmed.startsWith("["))
9
+ return trimmed.slice(1, trimmed.indexOf("]"));
10
+ const colon = trimmed.indexOf(":");
11
+ return colon === trimmed.lastIndexOf(":") && colon >= 0 ? trimmed.slice(0, colon) : trimmed;
12
+ };
13
+ const ownNames = () => {
14
+ const own = hostname().toLowerCase();
15
+ return own === "" ? [] : [own, `${own}.local`];
16
+ };
17
+ export function hostAllowed(host, rules) {
18
+ // An in-process request, or an HTTP/1.0 client, names no host.
19
+ if (host === undefined)
20
+ return true;
21
+ const name = nameOf(host);
22
+ if (name === "")
23
+ return false;
24
+ if (isIP(name) !== 0)
25
+ return true;
26
+ if (name === "localhost" || name.endsWith(".localhost"))
27
+ return true;
28
+ // This computer's tailnet name, also when the person serves Slopify through Tailscale
29
+ // themselves: a ts.net name only resolves inside a tailnet, never to someone's loopback.
30
+ if (name.endsWith(".ts.net"))
31
+ return true;
32
+ const named = [
33
+ rules.listenHost,
34
+ rules.remoteHost === undefined ? undefined : nameOf(rules.remoteHost),
35
+ ...rules.extraHosts,
36
+ ...ownNames(),
37
+ ].flatMap((one) => (one === undefined || one.trim() === "" ? [] : [one.trim().toLowerCase()]));
38
+ return named.includes(name);
39
+ }
40
+ const formTypes = ["text/plain", "application/x-www-form-urlencoded", "multipart/form-data"];
41
+ const safeMethods = ["GET", "HEAD", "OPTIONS"];
42
+ // The paired extension's own routes: token-checked, with CORS for its origin alone.
43
+ const extensionRoutes = /^\/api\/(?:studio|social)\/ext(?:\/|$)/;
44
+ export function requestGuard(deps) {
45
+ return async (c, next) => {
46
+ if (actorOf(c) !== "person")
47
+ return next();
48
+ const rules = {
49
+ listenHost: deps.listenHost,
50
+ remoteHost: deps.remoteHost?.(),
51
+ extraHosts: deps.extraHosts,
52
+ };
53
+ const host = c.req.header("host");
54
+ const forwarded = forwardedHost(c);
55
+ const refused = !hostAllowed(host, rules)
56
+ ? (host ?? "")
57
+ : forwarded !== undefined && !hostAllowed(forwarded, rules)
58
+ ? forwarded
59
+ : undefined;
60
+ if (refused !== undefined) {
61
+ const shown = refused.replace(/[^\w.:[\]-]/g, "").slice(0, 100);
62
+ return problem(c, {
63
+ status: 403,
64
+ detail: `Slopify refused this request: it was addressed to ${shown}, a name this Slopify doesn't answer to, which is how a web page could reach it through your browser. Open Slopify at http://127.0.0.1 and its port. If you reach it by this name on purpose, start Slopify with SLOPIFY_ALLOWED_HOSTS=${nameOf(shown)}.`,
65
+ extensions: { reason: "foreign-host" },
66
+ });
67
+ }
68
+ if (safeMethods.includes(c.req.method) ||
69
+ !c.req.path.startsWith("/api/") ||
70
+ extensionRoutes.test(c.req.path))
71
+ return next();
72
+ const origin = c.req.header("origin");
73
+ if (origin !== undefined) {
74
+ if (fromSlopifyPage(c, deps.remoteHost))
75
+ return next();
76
+ return problem(c, {
77
+ status: 403,
78
+ detail: `Slopify refused this request: it came from a web page at another address (${origin.slice(0, 100)}). Changes are made only from Slopify's own page; open Slopify and try again there.`,
79
+ extensions: { reason: "foreign-origin" },
80
+ });
81
+ }
82
+ const type = (c.req.header("content-type") ?? "").split(";")[0]?.trim().toLowerCase() ?? "";
83
+ if (formTypes.includes(type) && c.req.header("x-slopify-client") === undefined)
84
+ return problem(c, {
85
+ status: 403,
86
+ detail: "Slopify refused this request: it looks like a form another web page sent. Slopify's page sends its changes itself; open Slopify and try again there. A script on this computer can send the header X-Slopify-Client: script.",
87
+ extensions: { reason: "form-request" },
88
+ });
89
+ return next();
90
+ };
91
+ }
@@ -1,6 +1,6 @@
1
1
  import { zValidator } from "@hono/zod-validator";
2
2
  import { Hono } from "hono";
3
- import { z } from "zod";
3
+ import { errorText } from "../../kernel/errors.js";
4
4
  import { revisionControlSchema } from "../../slices/control/revision-control-schema.js";
5
5
  import { recoverProject } from "../../slices/rebuild/recovery.js";
6
6
  import { readRecovery } from "../../slices/rebuild/recovery-repo.js";
@@ -10,13 +10,11 @@ import { latestReviews } from "../../slices/reviews/view.js";
10
10
  import { currentRevisionId } from "../../slices/revisions/repo.js";
11
11
  import { getRevisionView } from "../../slices/revisions/view.js";
12
12
  import { actorOf } from "./actor.js";
13
- import { onInvalid, problem, titleOf } from "./problem.js";
14
- const id = z
15
- .string()
16
- .min(1)
17
- .max(64)
18
- .regex(/^[0-9A-Za-z_-]+$/);
19
- const projectParam = z.object({ id });
13
+ import { idParam, idString } from "./ids.js";
14
+ import { onInvalid, problem } from "./problem.js";
15
+ import { refusal } from "./refusal.js";
16
+ const id = idString;
17
+ const projectParam = idParam;
20
18
  const verdictParam = projectParam.extend({ verdictId: id });
21
19
  // The project page's review column: each reviewed item's latest verdict with its reasons,
22
20
  // Overrule to accept a failed item as it is, and Redo to have it made again through the
@@ -24,13 +22,11 @@ const verdictParam = projectParam.extend({ verdictId: id });
24
22
  export function reviewRoutes(deps) {
25
23
  const gone = (c) => problem(c, {
26
24
  status: 404,
27
- title: titleOf(404),
28
25
  detail: "This review no longer exists; the project may have been deleted or restored from a backup. Reload the project page.",
29
26
  });
30
27
  const decided = (c, verdict) => problem(c, {
31
28
  status: 409,
32
- title: titleOf(409),
33
- detail: `This verdict was already decided: the item was ${verdict.action === "overruled" ? "kept as it is" : "sent to be made again"}${verdict.actionBy === "agent" ? " by an agent" : ""}, so nothing changed. Reload the project page (or call needs_attention) to see what waits now.`,
29
+ detail: `This verdict was already decided: the item was ${verdict.action === "overruled" ? "kept as it is" : "sent to be made again"}${verdict.actionBy === "agent" ? " by an agent" : verdict.actionBy === "assistant" ? " by the assistant" : ""}, so nothing changed. Reload the project page (or call needs_attention) to see what waits now.`,
34
30
  extensions: { reason: "decided" },
35
31
  });
36
32
  const notify = (projectId) => {
@@ -40,7 +36,7 @@ export function reviewRoutes(deps) {
40
36
  catch (error) {
41
37
  deps.log.write("error", "review.notify", {
42
38
  projectId,
43
- detail: error instanceof Error ? error.message : String(error),
39
+ detail: errorText(error),
44
40
  });
45
41
  }
46
42
  };
@@ -62,7 +58,6 @@ export function reviewRoutes(deps) {
62
58
  const { id: projectId, verdictId } = c.req.valid("param");
63
59
  const busy = () => problem(c, {
64
60
  status: 409,
65
- title: titleOf(409),
66
61
  detail: "This verdict can't be overruled: it passed, or Slopify has already started making the item again. Wait for the new version to land, then reload the project page and overrule its review if it fails again.",
67
62
  extensions: { reason: "conflict" },
68
63
  });
@@ -105,12 +100,10 @@ export function reviewRoutes(deps) {
105
100
  if (deps.rebuild === undefined)
106
101
  return problem(c, {
107
102
  status: 503,
108
- title: titleOf(503),
109
103
  detail: "Redo is not ready yet because Slopify is still starting. Wait a moment, reload the page and try again.",
110
104
  });
111
105
  const already = () => problem(c, {
112
106
  status: 409,
113
- title: titleOf(409),
114
107
  detail: "Slopify is already making this item again. Wait for it to land, then reload the project page.",
115
108
  extensions: { reason: "conflict" },
116
109
  });
@@ -138,26 +131,20 @@ export function reviewRoutes(deps) {
138
131
  }
139
132
  });
140
133
  function refused(c, result, projectId) {
141
- const status = result.reason === "no-project"
142
- ? 404
143
- : result.reason === "invalid-selection" || result.reason === "invalid-edit"
144
- ? 400
145
- : 409;
146
134
  const lead = result.reason === "running"
147
135
  ? "This item, or work that depends on it, is still running. Wait for it to finish, or Pause the project, then use Redo again."
148
136
  : result.reason === "readiness"
149
- ? "Slopify can't make this item again yet. Fix what is listed in Settings → Providers or Edit project, then use Redo again."
137
+ ? "Slopify can't make this item again yet. Fix what is listed in Settings → Keys & services or Edit settings, then use Redo again."
150
138
  : result.reason === "conflict" || result.reason === "control-changed"
151
139
  ? "This project changed since the page loaded. Reload the project page, then use Redo again."
152
140
  : result.reason === "invalid-selection"
153
141
  ? "This item can't be made again."
154
142
  : "Slopify couldn't make this item again.";
155
- return problem(c, {
156
- status,
157
- title: titleOf(status),
143
+ return refusal(c, result.reason, {
144
+ statuses: { "no-project": 404, "invalid-selection": 400, "invalid-edit": 400 },
145
+ otherwise: 409,
158
146
  detail: [lead, ...(result.fields ?? []).map((row) => row.message)].join(" "),
159
147
  extensions: {
160
- reason: result.reason,
161
148
  fields: result.fields ?? [],
162
149
  currentRevisionId: currentRevisionId(deps.db, projectId) ?? null,
163
150
  },
@@ -4,13 +4,11 @@ import { z } from "zod";
4
4
  import { findRevisionDownload, revisionSetZip, } from "../../slices/revisions/downloads.js";
5
5
  import { fileResponse } from "./byte-range.js";
6
6
  import { replyForFolder } from "./folder-location.js";
7
- import { onInvalid, problem, titleOf } from "./problem.js";
7
+ import { idString } from "./ids.js";
8
+ import { onInvalid, problem } from "./problem.js";
9
+ import { fromSlopifyPage } from "./same-page.js";
8
10
  import { waveformAnswer } from "./waveform.js";
9
- const id = z
10
- .string()
11
- .min(1)
12
- .max(64)
13
- .regex(/^[0-9A-Za-z_-]+$/);
11
+ const id = idString;
14
12
  const revisionParam = z.object({ projectId: id, revisionId: id });
15
13
  const recordParam = revisionParam.extend({ recordId: id });
16
14
  const folderParam = z.object({ id, revisionId: id, recordId: id });
@@ -66,11 +64,9 @@ export function revisionFileRoutes(deps) {
66
64
  }
67
65
  export function revisionFolderRoutes(deps) {
68
66
  return new Hono().post("/:id/revisions/:revisionId/:recordId/open-folder", zValidator("param", folderParam, onInvalid), async (c) => {
69
- const origin = c.req.header("origin");
70
- if (origin !== undefined && origin !== new URL(c.req.url).origin)
67
+ if (!fromSlopifyPage(c, deps.remoteAccess?.remoteHost))
71
68
  return problem(c, {
72
69
  status: 403,
73
- title: titleOf(403),
74
70
  detail: "For your safety, folders can only be opened from the Slopify page itself. Open Slopify and try again there.",
75
71
  });
76
72
  const { id: projectId, revisionId, recordId } = c.req.valid("param");
@@ -83,9 +79,8 @@ export function revisionFolderRoutes(deps) {
83
79
  function unavailable(c, reason) {
84
80
  return problem(c, {
85
81
  status: 404,
86
- title: titleOf(404),
87
82
  detail: reason === "missing-file"
88
- ? "This file was deleted or moved from the project folder. In the Edit tab, use Choose what to remake (in the project's More menu) to make it again."
83
+ ? "This file was deleted or moved from the project folder. Use Choose what to make again (in the project's More menu) to make it again."
89
84
  : reason === "no-images"
90
85
  ? "There are no saved files of this kind to put in the zip yet. Wait for the step that makes them to finish, then download again."
91
86
  : "This file is no longer part of the project. Reload the page to see its current files.",
@@ -10,16 +10,14 @@ import { ensureBaseline, restoreRevision, saveRevision } from "../../slices/revi
10
10
  import { currentRevisionId, listRevisionHistory } from "../../slices/revisions/repo.js";
11
11
  import { restoreRevisionSchema, saveRevisionSchema } from "../../slices/revisions/schema.js";
12
12
  import { getRevisionView } from "../../slices/revisions/view.js";
13
- import { onInvalid, problem, titleOf } from "./problem.js";
14
- const id = z
15
- .string()
16
- .min(1)
17
- .max(64)
18
- .regex(/^[0-9A-Za-z_-]+$/);
19
- const projectParam = z.object({ id });
13
+ import { idParam, idString } from "./ids.js";
14
+ import { onInvalid, problem } from "./problem.js";
15
+ import { refusal } from "./refusal.js";
16
+ const id = idString;
17
+ const projectParam = idParam;
20
18
  const revisionParam = projectParam.extend({ revisionId: id });
21
- const saveBody = saveRevisionSchema.extend({ idempotencyKey: z.uuid() });
22
- const restoreBody = restoreRevisionSchema.extend({ idempotencyKey: z.uuid() });
19
+ export const saveBody = saveRevisionSchema.extend({ idempotencyKey: z.uuid() });
20
+ export const restoreBody = restoreRevisionSchema.extend({ idempotencyKey: z.uuid() });
23
21
  const statuses = {
24
22
  "no-project": 404,
25
23
  "no-revision": 404,
@@ -38,20 +36,18 @@ const details = {
38
36
  conflict: "This project changed while you were editing. Reload the page to see the latest, then make your change again.",
39
37
  "idempotency-conflict": "This change was already sent with different details. Reload the page, then try again.",
40
38
  "invalid-edit": "Some of your changes are not valid. Fix the highlighted fields and save again.",
41
- "stale-preview": "What needs rebuilding changed since you opened this review. Cancel it and open Choose what to remake (in the project's More menu) again to see the latest.",
39
+ "stale-preview": "What needs rebuilding changed since you opened this review. Cancel it and open Choose what to make again (in the project's More menu) again to see the latest.",
42
40
  "invalid-selection": "Some of the outputs you picked can no longer be rebuilt. Reload the page and choose again.",
43
- "review-required": 'This rebuild touches content you supplied yourself. Tick "Keep the provided content" for each item in the review, then choose Start rebuild.',
44
- "cost-ack-required": "Some costs for this rebuild are unknown. Tick the box confirming you understand, then choose Start rebuild.",
45
- readiness: "Slopify cannot run this rebuild yet. Fix the problems listed, then try again.",
41
+ "review-required": "This touches content you supplied yourself. Tick “Keep the provided content” for each item in the review, then press the button that makes them again.",
42
+ "cost-ack-required": "Some prices aren't known in advance. Tick “Start even though …” in the review, then press the button that makes them again.",
43
+ readiness: "This can't start yet. Fix the items listed, then try again.",
46
44
  };
47
45
  function refused(c, deps, projectId, value) {
48
- const status = statuses[value.reason];
49
- return problem(c, {
50
- status,
51
- title: titleOf(status),
46
+ return refusal(c, value.reason, {
47
+ statuses,
48
+ otherwise: 409,
52
49
  detail: details[value.reason],
53
50
  extensions: {
54
- reason: value.reason,
55
51
  currentRevisionId: currentRevisionId(deps.db, projectId) ?? null,
56
52
  fields: value.fields ?? [],
57
53
  },
@@ -60,7 +56,6 @@ function refused(c, deps, projectId, value) {
60
56
  function unavailable(c) {
61
57
  return problem(c, {
62
58
  status: 503,
63
- title: titleOf(503),
64
59
  detail: "Rebuilding is not ready yet because Slopify is still starting. Wait a moment, reload the page and try again.",
65
60
  });
66
61
  }
@@ -3,7 +3,7 @@ import { Hono } from "hono";
3
3
  import { z } from "zod";
4
4
  import { projectExists } from "../../slices/admission/repo.js";
5
5
  import { runCostOf } from "../../slices/run-cost/panel.js";
6
- import { onInvalid, problem, titleOf } from "./problem.js";
6
+ import { onInvalid, problem } from "./problem.js";
7
7
  const idParam = z.object({ id: z.string().min(1).max(64) });
8
8
  // The project's Run cost tab and its "Waiting for … limits" line: what the run's provider
9
9
  // calls used and cost, and any CLI plan limit a stage is waiting on. Read from the stored
@@ -17,7 +17,6 @@ export function runCostRoutes(deps) {
17
17
  if (!projectExists(deps.db, id))
18
18
  return problem(c, {
19
19
  status: 404,
20
- title: titleOf(404),
21
20
  detail: "This project no longer exists. Go back to Projects to pick another.",
22
21
  });
23
22
  return c.json(runCostOf(deps.db, id));
@@ -0,0 +1,80 @@
1
+ // The address of what connected to this server: the browser, or a proxy in front of it.
2
+ // Undefined for an in-process request, which has no socket.
3
+ export function peerAddress(c) {
4
+ const env = c.env;
5
+ if (typeof env !== "object" || env === null || !("incoming" in env))
6
+ return undefined;
7
+ const incoming = env.incoming;
8
+ if (typeof incoming !== "object" || incoming === null || !("socket" in incoming))
9
+ return undefined;
10
+ const socket = incoming.socket;
11
+ if (typeof socket !== "object" || socket === null || !("remoteAddress" in socket))
12
+ return undefined;
13
+ return typeof socket.remoteAddress === "string" ? socket.remoteAddress : undefined;
14
+ }
15
+ const loopbackPeer = (address) => address !== undefined &&
16
+ (address === "::1" || /^(?:::ffff:)?127\.\d{1,3}\.\d{1,3}\.\d{1,3}$/u.test(address));
17
+ // The name the browser used, as a proxy on this computer passes it on in X-Forwarded-Host
18
+ // (its first value). Only trusted from a loopback peer: anyone else could write the header.
19
+ export function forwardedHost(c) {
20
+ if (!loopbackPeer(peerAddress(c)))
21
+ return undefined;
22
+ const first = c.req.header("x-forwarded-host")?.split(",")[0]?.trim();
23
+ return first === undefined || first === "" ? undefined : first;
24
+ }
25
+ // host[:port] with the scheme's default port left out, as an Origin's own host has it.
26
+ function hostOf(host, scheme) {
27
+ const lower = host.trim().toLowerCase();
28
+ const fallback = scheme === "https:" ? ":443" : scheme === "http:" ? ":80" : undefined;
29
+ return fallback !== undefined && lower.endsWith(fallback)
30
+ ? lower.slice(0, -fallback.length)
31
+ : lower;
32
+ }
33
+ // Standard same-origin protection: a browser's Origin names the page that sent the request,
34
+ // and a page at the very host:port the browser addressed is Slopify's own, whatever proxy
35
+ // (Slopify's remote access, a `tailscale serve` set up by hand, Caddy, nginx) stands in front.
36
+ // A cross-site page names its own host, so it never matches; which names may reach Slopify at
37
+ // all is `request-guard.ts`'s Host check.
38
+ function sameHost(c, origin) {
39
+ let url;
40
+ try {
41
+ url = new URL(origin);
42
+ }
43
+ catch {
44
+ return false;
45
+ }
46
+ if (url.host === "")
47
+ return false;
48
+ const own = url.host.toLowerCase();
49
+ return [c.req.header("host"), forwardedHost(c)].some((host) => host !== undefined && hostOf(host, url.protocol) === own);
50
+ }
51
+ // Whether a request with an Origin comes from the Slopify page itself: the page this server
52
+ // serves, the page at the address the browser used for this request (through any proxy), or
53
+ // the same page at the exact https address Slopify set up in Tailscale.
54
+ export function fromSlopifyPage(c, remoteHost) {
55
+ const origin = c.req.header("origin");
56
+ if (origin === undefined || origin === new URL(c.req.url).origin)
57
+ return true;
58
+ if (sameHost(c, origin))
59
+ return true;
60
+ const remote = remoteHost?.();
61
+ return (remote !== undefined &&
62
+ origin === `https://${remote}` &&
63
+ (c.req.header("x-forwarded-host") ?? c.req.header("host")) === remote);
64
+ }
65
+ // A request that reached Slopify through a forwarding proxy, Tailscale serve included: it sets
66
+ // X-Forwarded-For on every request and keeps the ts.net Host. Agents' endpoints answer only
67
+ // requests made on this computer.
68
+ export function viaProxy(headers) {
69
+ if (headers.has("x-forwarded-for") || headers.has("x-forwarded-host"))
70
+ return true;
71
+ if (headers.has("tailscale-user-login") || headers.has("tailscale-funnel-request"))
72
+ return true;
73
+ const host = headers.get("host") ?? "";
74
+ const name = host.replace(/:\d+$/u, "").toLowerCase();
75
+ return name.endsWith(".ts.net");
76
+ }
77
+ // Tailscale marks every request that came through Funnel, from the public internet.
78
+ export function viaFunnel(headers) {
79
+ return headers.has("tailscale-funnel-request");
80
+ }