@hraness/slopcamera 3.2.4

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 (441) hide show
  1. package/LICENSE +21 -0
  2. package/NOTICE.md +60 -0
  3. package/PRIVACY.md +46 -0
  4. package/README.md +311 -0
  5. package/SECURITY.md +21 -0
  6. package/apps/desktop/README.md +966 -0
  7. package/apps/desktop/analysis/protocol.ts +223 -0
  8. package/apps/desktop/application/capability-binding.ts +594 -0
  9. package/apps/desktop/application/context.ts +97 -0
  10. package/apps/desktop/application/creative-iteration.ts +1385 -0
  11. package/apps/desktop/application/default-registry.ts +146 -0
  12. package/apps/desktop/application/directing-contract.ts +218 -0
  13. package/apps/desktop/application/directing-plan.ts +178 -0
  14. package/apps/desktop/application/errors.ts +18 -0
  15. package/apps/desktop/application/gateway-port.ts +867 -0
  16. package/apps/desktop/application/html-overlay-browser-runtime.ts +687 -0
  17. package/apps/desktop/application/html-overlay-integrity.ts +337 -0
  18. package/apps/desktop/application/html-overlay-renderer.ts +45 -0
  19. package/apps/desktop/application/index.ts +13 -0
  20. package/apps/desktop/application/operation-completion-checkpoint.ts +333 -0
  21. package/apps/desktop/application/operation-effects.ts +94 -0
  22. package/apps/desktop/application/operation.ts +224 -0
  23. package/apps/desktop/application/operations/analysis/faces.ts +244 -0
  24. package/apps/desktop/application/operations/analysis/music.ts +248 -0
  25. package/apps/desktop/application/operations/analysis/project-auto-zooms.ts +302 -0
  26. package/apps/desktop/application/operations/analysis/project-inactivity.ts +381 -0
  27. package/apps/desktop/application/operations/analysis/scenes.ts +272 -0
  28. package/apps/desktop/application/operations/analysis/shared.ts +202 -0
  29. package/apps/desktop/application/operations/derive/edit-batch.ts +967 -0
  30. package/apps/desktop/application/operations/derive/follow-faces.ts +454 -0
  31. package/apps/desktop/application/operations/edit/bind-candidate-revision.ts +91 -0
  32. package/apps/desktop/application/operations/edit/create-candidate-revision.ts +377 -0
  33. package/apps/desktop/application/operations/edit/create-revision.ts +255 -0
  34. package/apps/desktop/application/operations/edit/freeze-revision.ts +182 -0
  35. package/apps/desktop/application/operations/gateway/image.ts +41 -0
  36. package/apps/desktop/application/operations/gateway/index.ts +17 -0
  37. package/apps/desktop/application/operations/gateway/shared.ts +122 -0
  38. package/apps/desktop/application/operations/gateway/speech.ts +41 -0
  39. package/apps/desktop/application/operations/gateway/transcription.ts +42 -0
  40. package/apps/desktop/application/operations/gateway/video.ts +41 -0
  41. package/apps/desktop/application/operations/index.ts +31 -0
  42. package/apps/desktop/application/operations/iteration/create-candidate.ts +240 -0
  43. package/apps/desktop/application/operations/iteration/create-matrix.ts +117 -0
  44. package/apps/desktop/application/operations/iteration/select.ts +155 -0
  45. package/apps/desktop/application/operations/iteration/shared.ts +250 -0
  46. package/apps/desktop/application/operations/media/audio-effects.ts +376 -0
  47. package/apps/desktop/application/operations/media/binding.ts +79 -0
  48. package/apps/desktop/application/operations/media/capabilities.ts +112 -0
  49. package/apps/desktop/application/operations/media/color-grade.ts +343 -0
  50. package/apps/desktop/application/operations/media/html-overlay.ts +802 -0
  51. package/apps/desktop/application/operations/media/index.ts +8 -0
  52. package/apps/desktop/application/operations/media/ingest.ts +303 -0
  53. package/apps/desktop/application/operations/media/overlay.ts +729 -0
  54. package/apps/desktop/application/operations/media/shared.ts +845 -0
  55. package/apps/desktop/application/operations/media/transform-platform.ts +120 -0
  56. package/apps/desktop/application/operations/project/commit-edits.ts +2054 -0
  57. package/apps/desktop/application/operations/project/promote-selection.ts +495 -0
  58. package/apps/desktop/application/operations/project/snapshot.ts +95 -0
  59. package/apps/desktop/application/operations/recording/index.ts +5 -0
  60. package/apps/desktop/application/operations/recording/pause.ts +34 -0
  61. package/apps/desktop/application/operations/recording/resume.ts +34 -0
  62. package/apps/desktop/application/operations/recording/shared.ts +199 -0
  63. package/apps/desktop/application/operations/recording/start.ts +169 -0
  64. package/apps/desktop/application/operations/recording/stop.ts +34 -0
  65. package/apps/desktop/application/operations/render/bind-candidate-output.ts +324 -0
  66. package/apps/desktop/application/operations/render/materialize-selection.ts +322 -0
  67. package/apps/desktop/application/operations/render/project-caption-plan.ts +287 -0
  68. package/apps/desktop/application/operations/render/project-plan.ts +939 -0
  69. package/apps/desktop/application/operations/render/project-spatial-receipt.ts +70 -0
  70. package/apps/desktop/application/operations/render/project.ts +1922 -0
  71. package/apps/desktop/application/operations/shared.ts +67 -0
  72. package/apps/desktop/application/operations/slopcamera-portable.ts +1188 -0
  73. package/apps/desktop/application/operations/slopcamera-visuals.ts +784 -0
  74. package/apps/desktop/application/operations/spatial-project.ts +209 -0
  75. package/apps/desktop/application/operations/spatial-render.ts +415 -0
  76. package/apps/desktop/application/operations/spatial-scene.ts +97 -0
  77. package/apps/desktop/application/operations/studio.ts +143 -0
  78. package/apps/desktop/application/output-publication-lease.ts +163 -0
  79. package/apps/desktop/application/project-publication-lease.ts +233 -0
  80. package/apps/desktop/application/project-store.ts +196 -0
  81. package/apps/desktop/application/receipts.ts +533 -0
  82. package/apps/desktop/application/registry.ts +182 -0
  83. package/apps/desktop/application/spatial-assets.ts +675 -0
  84. package/apps/desktop/application/spatial-compositor-cadence.ts +23 -0
  85. package/apps/desktop/application/spatial-project-authority.ts +35 -0
  86. package/apps/desktop/application/spatial-project-lease.ts +126 -0
  87. package/apps/desktop/application/spatial-project-render-preparation.ts +225 -0
  88. package/apps/desktop/application/spatial-project-store.ts +286 -0
  89. package/apps/desktop/application/spatial-render-projection.ts +201 -0
  90. package/apps/desktop/application/spatial-render.ts +677 -0
  91. package/apps/desktop/application/spatial-spz.ts +44 -0
  92. package/apps/desktop/application/spatial-world-import.ts +135 -0
  93. package/apps/desktop/application/spatial-world-provenance.ts +46 -0
  94. package/apps/desktop/application/studio-port.ts +58 -0
  95. package/apps/desktop/application/verified-receipt-reconciliation.ts +1574 -0
  96. package/apps/desktop/capture/protocol.ts +689 -0
  97. package/apps/desktop/cli/analyzer.ts +406 -0
  98. package/apps/desktop/cli/args.ts +3181 -0
  99. package/apps/desktop/cli/asset-ingest.ts +747 -0
  100. package/apps/desktop/cli/atomic-render-effects.ts +210 -0
  101. package/apps/desktop/cli/atomic-render-platform.ts +205 -0
  102. package/apps/desktop/cli/atomic-render.ts +50 -0
  103. package/apps/desktop/cli/audio-analysis.ts +252 -0
  104. package/apps/desktop/cli/bounded-map.ts +39 -0
  105. package/apps/desktop/cli/bundle-service.ts +256 -0
  106. package/apps/desktop/cli/capabilities.ts +212 -0
  107. package/apps/desktop/cli/capture-bundle.ts +1117 -0
  108. package/apps/desktop/cli/command-host-resources.ts +251 -0
  109. package/apps/desktop/cli/commands.ts +6836 -0
  110. package/apps/desktop/cli/directing-blob.ts +291 -0
  111. package/apps/desktop/cli/directing-media.ts +517 -0
  112. package/apps/desktop/cli/directing-quote.ts +89 -0
  113. package/apps/desktop/cli/directing-service.ts +288 -0
  114. package/apps/desktop/cli/emoji-assets.ts +538 -0
  115. package/apps/desktop/cli/errors.ts +29 -0
  116. package/apps/desktop/cli/face-analysis-service.ts +940 -0
  117. package/apps/desktop/cli/filter-script.ts +76 -0
  118. package/apps/desktop/cli/gateway-application-port.ts +1142 -0
  119. package/apps/desktop/cli/gateway-credential.ts +81 -0
  120. package/apps/desktop/cli/gateway-media-artifacts.ts +723 -0
  121. package/apps/desktop/cli/gateway-media-catalog.ts +922 -0
  122. package/apps/desktop/cli/gateway-media-service.ts +3106 -0
  123. package/apps/desktop/cli/gateway-media-signature.ts +122 -0
  124. package/apps/desktop/cli/gateway-provider-options.ts +562 -0
  125. package/apps/desktop/cli/gateway-scene-provider.ts +391 -0
  126. package/apps/desktop/cli/help.ts +501 -0
  127. package/apps/desktop/cli/html-overlay-gpu.ts +97 -0
  128. package/apps/desktop/cli/html-overlay-renderer.ts +2478 -0
  129. package/apps/desktop/cli/io.ts +290 -0
  130. package/apps/desktop/cli/main.ts +123 -0
  131. package/apps/desktop/cli/media-effects-service.ts +885 -0
  132. package/apps/desktop/cli/media-ingest-model.ts +364 -0
  133. package/apps/desktop/cli/media-ingest-platform.ts +227 -0
  134. package/apps/desktop/cli/media-ingest-program.ts +200 -0
  135. package/apps/desktop/cli/media-ingest.ts +30 -0
  136. package/apps/desktop/cli/music-analysis-service.ts +434 -0
  137. package/apps/desktop/cli/mutation-lock.ts +530 -0
  138. package/apps/desktop/cli/native-assets.d.ts +12 -0
  139. package/apps/desktop/cli/native-media-runtime.macos.ts +7 -0
  140. package/apps/desktop/cli/overlay-playback.ts +177 -0
  141. package/apps/desktop/cli/paths.ts +265 -0
  142. package/apps/desktop/cli/portable-surface.ts +242 -0
  143. package/apps/desktop/cli/project-camera-receipt.ts +112 -0
  144. package/apps/desktop/cli/project-face-camera.ts +394 -0
  145. package/apps/desktop/cli/project-inactivity-service.ts +552 -0
  146. package/apps/desktop/cli/project-media-integrity.ts +121 -0
  147. package/apps/desktop/cli/project-renderer.ts +1526 -0
  148. package/apps/desktop/cli/project-service.ts +519 -0
  149. package/apps/desktop/cli/project-state-transaction.ts +520 -0
  150. package/apps/desktop/cli/query-limits.ts +1 -0
  151. package/apps/desktop/cli/recording-controller.ts +1567 -0
  152. package/apps/desktop/cli/recording-daemon.ts +931 -0
  153. package/apps/desktop/cli/recording-ref.ts +56 -0
  154. package/apps/desktop/cli/renamed-environment.ts +6 -0
  155. package/apps/desktop/cli/renderer.ts +1439 -0
  156. package/apps/desktop/cli/run-cli-test-helper.ts +60 -0
  157. package/apps/desktop/cli/scene-analysis-service.ts +879 -0
  158. package/apps/desktop/cli/spatial-compositor-verifier.ts +213 -0
  159. package/apps/desktop/cli/spatial-project-service.ts +90 -0
  160. package/apps/desktop/cli/spatial-scene-service.ts +105 -0
  161. package/apps/desktop/cli/spatial-world-service.ts +39 -0
  162. package/apps/desktop/cli/speech-analysis-service.ts +682 -0
  163. package/apps/desktop/cli/studio-assemble.ts +58 -0
  164. package/apps/desktop/cli/studio-command.ts +60 -0
  165. package/apps/desktop/cli/studio-custody.ts +60 -0
  166. package/apps/desktop/cli/studio-encode.ts +216 -0
  167. package/apps/desktop/cli/studio-exr.ts +52 -0
  168. package/apps/desktop/cli/studio-files.ts +179 -0
  169. package/apps/desktop/cli/studio-output-validation.ts +162 -0
  170. package/apps/desktop/cli/studio-process.ts +145 -0
  171. package/apps/desktop/cli/studio-runtime.ts +77 -0
  172. package/apps/desktop/cli/studio-scaffold.ts +70 -0
  173. package/apps/desktop/cli/studio-service.ts +301 -0
  174. package/apps/desktop/cli/studio-spatial-asset.ts +248 -0
  175. package/apps/desktop/cli/studio-template-names.ts +3 -0
  176. package/apps/desktop/cli/studio-workflow.ts +19 -0
  177. package/apps/desktop/cli/time.ts +25 -0
  178. package/apps/desktop/cli/workflow-code.ts +584 -0
  179. package/apps/desktop/cli/workflow-runs.ts +576 -0
  180. package/apps/desktop/code/advanced.ts +139 -0
  181. package/apps/desktop/code/application-node-planner.ts +1288 -0
  182. package/apps/desktop/code/compiler.ts +329 -0
  183. package/apps/desktop/code/contracts.ts +13 -0
  184. package/apps/desktop/code/creative-recipes.ts +94 -0
  185. package/apps/desktop/code/define-workflow.ts +115 -0
  186. package/apps/desktop/code/editing.ts +284 -0
  187. package/apps/desktop/code/file-candidate-provenance.ts +420 -0
  188. package/apps/desktop/code/file-candidate.ts +69 -0
  189. package/apps/desktop/code/graph-builder.ts +10 -0
  190. package/apps/desktop/code/host-resource-policy.ts +55 -0
  191. package/apps/desktop/code/plan-contracts.ts +159 -0
  192. package/apps/desktop/code/planning.ts +361 -0
  193. package/apps/desktop/code/public.ts +134 -0
  194. package/apps/desktop/code/reference-projection.ts +62 -0
  195. package/apps/desktop/code/run-contracts.ts +466 -0
  196. package/apps/desktop/code/run-store.ts +2478 -0
  197. package/apps/desktop/code/runtime-identity.ts +328 -0
  198. package/apps/desktop/code/scheduler.ts +3067 -0
  199. package/apps/desktop/code/semantic-builder.ts +1436 -0
  200. package/apps/desktop/code/source-bundle.ts +1641 -0
  201. package/apps/desktop/code/source-typecheck.ts +211 -0
  202. package/apps/desktop/code/testing.ts +79 -0
  203. package/apps/desktop/code/worker-client.ts +1675 -0
  204. package/apps/desktop/code/worker-effects.ts +141 -0
  205. package/apps/desktop/code/worker-entry.ts +435 -0
  206. package/apps/desktop/code/worker-lease-guardian.ts +179 -0
  207. package/apps/desktop/code/worker-process-identity.ts +484 -0
  208. package/apps/desktop/code/worker-protocol.ts +211 -0
  209. package/apps/desktop/code/workflow-effects.ts +132 -0
  210. package/apps/desktop/contracts/analysis.ts +949 -0
  211. package/apps/desktop/contracts/edit.ts +576 -0
  212. package/apps/desktop/contracts/index.ts +9 -0
  213. package/apps/desktop/contracts/media-effects.ts +139 -0
  214. package/apps/desktop/contracts/project-render.ts +644 -0
  215. package/apps/desktop/contracts/project.ts +740 -0
  216. package/apps/desktop/contracts/recording.ts +1434 -0
  217. package/apps/desktop/contracts/render.ts +334 -0
  218. package/apps/desktop/contracts/runtime.ts +155 -0
  219. package/apps/desktop/contracts/spatial-compositor.ts +58 -0
  220. package/apps/desktop/contracts/spatial-project.ts +159 -0
  221. package/apps/desktop/contracts/spatial-world.ts +48 -0
  222. package/apps/desktop/contracts/video-effects.ts +120 -0
  223. package/apps/desktop/core/alignment-apply.ts +400 -0
  224. package/apps/desktop/core/audio-alignment.ts +466 -0
  225. package/apps/desktop/core/auto-zoom.ts +209 -0
  226. package/apps/desktop/core/canonical-json.ts +14 -0
  227. package/apps/desktop/core/emoji.ts +210 -0
  228. package/apps/desktop/core/events.ts +409 -0
  229. package/apps/desktop/core/face-framing.ts +533 -0
  230. package/apps/desktop/core/face-tracks.ts +326 -0
  231. package/apps/desktop/core/inactivity.ts +137 -0
  232. package/apps/desktop/core/index.ts +30 -0
  233. package/apps/desktop/core/intervals.ts +123 -0
  234. package/apps/desktop/core/montage.ts +236 -0
  235. package/apps/desktop/core/music-analysis.ts +518 -0
  236. package/apps/desktop/core/plan.ts +244 -0
  237. package/apps/desktop/core/project-auto-zoom.ts +75 -0
  238. package/apps/desktop/core/project-camera-spatial.ts +676 -0
  239. package/apps/desktop/core/project-camera.ts +403 -0
  240. package/apps/desktop/core/project-captions.ts +888 -0
  241. package/apps/desktop/core/project-inactivity.ts +125 -0
  242. package/apps/desktop/core/project-metadata.ts +927 -0
  243. package/apps/desktop/core/project-plan.ts +375 -0
  244. package/apps/desktop/core/project-render-plan.ts +303 -0
  245. package/apps/desktop/core/project-time.ts +469 -0
  246. package/apps/desktop/core/render-plan.ts +442 -0
  247. package/apps/desktop/core/scene-cache.ts +174 -0
  248. package/apps/desktop/core/scene-sampling.ts +448 -0
  249. package/apps/desktop/core/spatial-compositor.ts +22 -0
  250. package/apps/desktop/core/spatial-durability.ts +99 -0
  251. package/apps/desktop/core/spatial-project.ts +169 -0
  252. package/apps/desktop/core/spatial-shot-clock.ts +32 -0
  253. package/apps/desktop/core/speech-analysis.ts +333 -0
  254. package/apps/desktop/core/storage.ts +970 -0
  255. package/apps/desktop/core/time-map.ts +91 -0
  256. package/apps/desktop/core/time.ts +61 -0
  257. package/apps/desktop/core/video-effects.ts +660 -0
  258. package/apps/desktop/dist/cli/NebulaSans-Bold-26se8aek.otf +0 -0
  259. package/apps/desktop/dist/cli/NebulaSans-Bold-bcz7y08t.woff2 +0 -0
  260. package/apps/desktop/dist/cli/NebulaSans-Book-5ax05zvn.woff2 +0 -0
  261. package/apps/desktop/dist/cli/NebulaSans-Book-8cenzchw.otf +0 -0
  262. package/apps/desktop/dist/cli/main.js +5086 -0
  263. package/apps/desktop/html-overlay/catalog.ts +171 -0
  264. package/apps/desktop/html-overlay/contracts.ts +372 -0
  265. package/apps/desktop/html-overlay/execution-profile.ts +77 -0
  266. package/apps/desktop/html-overlay/index.ts +98 -0
  267. package/apps/desktop/html-overlay/libraries.ts +263 -0
  268. package/apps/desktop/html-overlay/random.ts +49 -0
  269. package/apps/desktop/html-overlay/runtime.ts +813 -0
  270. package/apps/desktop/html-overlay/scaffolds.ts +792 -0
  271. package/apps/desktop/html-overlay/spark-runtime.ts +88 -0
  272. package/apps/desktop/html-overlay/spatial-splat-runtime.ts +60 -0
  273. package/apps/desktop/html-overlay/spatial.ts +720 -0
  274. package/apps/desktop/studio/assets/README.md +39 -0
  275. package/apps/desktop/studio/assets/contracts.ts +129 -0
  276. package/apps/desktop/studio/assets/network.ts +64 -0
  277. package/apps/desktop/studio/assets/poly-haven.ts +148 -0
  278. package/apps/desktop/studio/drivers/blender_driver.py +466 -0
  279. package/apps/desktop/studio/drivers/cadquery_driver.py +176 -0
  280. package/apps/desktop/studio/education/driver.py +308 -0
  281. package/apps/desktop/studio/education/lesson.py +180 -0
  282. package/apps/desktop/studio/education/toolkit.py +249 -0
  283. package/apps/desktop/workflows/chaptered-demo.ts +48 -0
  284. package/apps/desktop/workflows/creative-iteration.ts +107 -0
  285. package/apps/desktop/workflows/creative-selection.ts +92 -0
  286. package/apps/desktop/workflows/fragments.ts +486 -0
  287. package/apps/desktop/workflows/index.ts +93 -0
  288. package/apps/desktop/workflows/polished-screen-demo.ts +128 -0
  289. package/apps/desktop/workflows/scene-direction.ts +12 -0
  290. package/apps/desktop/workflows/social-variants.ts +75 -0
  291. package/apps/desktop/workflows/talking-head-cleanup.ts +53 -0
  292. package/dist/NebulaSans-Bold-26se8aek.otf +0 -0
  293. package/dist/NebulaSans-Bold-bcz7y08t.woff2 +0 -0
  294. package/dist/NebulaSans-Book-5ax05zvn.woff2 +0 -0
  295. package/dist/NebulaSans-Book-8cenzchw.otf +0 -0
  296. package/dist/cli.js +479 -0
  297. package/dist/code/advanced.js +270 -0
  298. package/dist/code/index.js +2240 -0
  299. package/dist/generate.js +24 -0
  300. package/dist/host-resources.js +26 -0
  301. package/dist/index-42zsesc1.js +2257 -0
  302. package/dist/index-7308egqr.js +48 -0
  303. package/dist/index-8txs6fkn.js +712 -0
  304. package/dist/index-mcy8z0br.js +192 -0
  305. package/dist/index-p63wavx0.js +2029 -0
  306. package/dist/index-r7gdhmsp.js +424 -0
  307. package/dist/index-sh6xbav6.js +1219 -0
  308. package/dist/index-z1w83f81.js +4 -0
  309. package/dist/index-z7239b4h.js +2363 -0
  310. package/dist/index-zfeg9hmw.js +2013 -0
  311. package/dist/index.js +241 -0
  312. package/dist/operations.js +29 -0
  313. package/dist/skill-install-dh0ntqba.js +10 -0
  314. package/dist/vectorize/worker.js +142 -0
  315. package/dist/workflow.js +16 -0
  316. package/docs/README.md +38 -0
  317. package/docs/studio.md +215 -0
  318. package/examples/studio/blender/README.md +26 -0
  319. package/examples/studio/blender/character.py +152 -0
  320. package/examples/studio/blender/cloth.py +101 -0
  321. package/examples/studio/blender/color_chart.py +29 -0
  322. package/examples/studio/blender/fluid.py +78 -0
  323. package/examples/studio/blender/import_model.py +74 -0
  324. package/examples/studio/blender/product.py +53 -0
  325. package/examples/studio/blender/shaded_street.py +481 -0
  326. package/examples/studio/blender/studio_scene.py +147 -0
  327. package/examples/studio/cadquery/README.md +13 -0
  328. package/examples/studio/cadquery/bracket.py +36 -0
  329. package/examples/studio/cadquery/import_step.py +12 -0
  330. package/examples/studio/education/README.md +46 -0
  331. package/examples/studio/education/lesson.json +99 -0
  332. package/examples/studio/education/scene.py +7 -0
  333. package/examples/studio/hybrid-scene.ts +96 -0
  334. package/examples/studio/native-workflow.ts +14 -0
  335. package/examples/studio/vgpu/README.md +64 -0
  336. package/examples/studio/vgpu/heat-field.mjs +290 -0
  337. package/package.json +304 -0
  338. package/packages/scene/src/index.ts +1 -0
  339. package/packages/scene/src/scene.ts +215 -0
  340. package/schema/diagram.schema.json +334 -0
  341. package/skills/slopcamera/SKILL.md +45 -0
  342. package/skills/slopcamera/agents/openai.yaml +4 -0
  343. package/skills/slopcamera/references/customization.md +77 -0
  344. package/skills/slopcamera/references/diagrams.md +153 -0
  345. package/skills/slopcamera/references/directed-scenes.md +42 -0
  346. package/skills/slopcamera/references/directing-video.md +18 -0
  347. package/skills/slopcamera/references/educational-video.md +26 -0
  348. package/skills/slopcamera/references/gateway-media.md +134 -0
  349. package/skills/slopcamera/references/install.md +62 -0
  350. package/skills/slopcamera/references/native-studio.md +31 -0
  351. package/skills/slopcamera/references/reference-led-3d.md +140 -0
  352. package/skills/slopcamera/references/rubber-stamp-examples/poster-example-1.jpg +0 -0
  353. package/skills/slopcamera/references/rubber-stamp-examples/poster-example-2.jpg +0 -0
  354. package/skills/slopcamera/references/rubber-stamp-examples/stamp-style-1.png +0 -0
  355. package/skills/slopcamera/references/rubber-stamp-examples/stamp-style-2.png +0 -0
  356. package/skills/slopcamera/references/rubber-stamp-field-notes.md +144 -0
  357. package/skills/slopcamera/references/vectorization.md +29 -0
  358. package/skills/slopcamera/references/video-projects.md +187 -0
  359. package/skills/slopcamera/references/visual-communication.md +120 -0
  360. package/skills/slopcamera/references/workflows-sdk.md +45 -0
  361. package/skills/slopcamera/scripts/compose-rubber-stamp-field-note.ts +229 -0
  362. package/src/artifacts.ts +106 -0
  363. package/src/assets/fonts/nebula-sans/LICENSE.txt +96 -0
  364. package/src/assets/fonts/nebula-sans/NebulaSans-Bold.otf +0 -0
  365. package/src/assets/fonts/nebula-sans/NebulaSans-Bold.woff2 +0 -0
  366. package/src/assets/fonts/nebula-sans/NebulaSans-Book.otf +0 -0
  367. package/src/assets/fonts/nebula-sans/NebulaSans-Book.woff2 +0 -0
  368. package/src/assets/fonts/nebula-sans/PROVENANCE.md +21 -0
  369. package/src/assets.d.ts +9 -0
  370. package/src/cli.ts +601 -0
  371. package/src/cloud-errors.ts +26 -0
  372. package/src/code/advanced.ts +10 -0
  373. package/src/code/boundary.ts +21 -0
  374. package/src/code/canonical-json.ts +129 -0
  375. package/src/code/compiler.ts +953 -0
  376. package/src/code/contracts.ts +576 -0
  377. package/src/code/define-workflow.ts +334 -0
  378. package/src/code/errors.ts +41 -0
  379. package/src/code/graph-builder.ts +945 -0
  380. package/src/code/index.ts +121 -0
  381. package/src/code/json-snapshot.ts +899 -0
  382. package/src/code/json-utf8.ts +88 -0
  383. package/src/code/portable-builder.ts +136 -0
  384. package/src/code/projection.ts +378 -0
  385. package/src/code/public-operations.ts +450 -0
  386. package/src/code/runtime.ts +580 -0
  387. package/src/code/sha256.ts +36 -0
  388. package/src/config.ts +180 -0
  389. package/src/desktop.ts +253 -0
  390. package/src/fs.ts +10 -0
  391. package/src/generate.ts +704 -0
  392. package/src/host-resource-posix.ts +161 -0
  393. package/src/host-resources.ts +1749 -0
  394. package/src/icons.ts +58 -0
  395. package/src/index.ts +200 -0
  396. package/src/label-layout.ts +130 -0
  397. package/src/layout.ts +413 -0
  398. package/src/lint.ts +127 -0
  399. package/src/mcp/boundary.ts +354 -0
  400. package/src/mcp/index.ts +33 -0
  401. package/src/mcp/server.ts +272 -0
  402. package/src/mcp/tools.ts +963 -0
  403. package/src/mcp/types.ts +71 -0
  404. package/src/operations.ts +775 -0
  405. package/src/parse.ts +861 -0
  406. package/src/process-environment.ts +17 -0
  407. package/src/render.ts +437 -0
  408. package/src/skill-install.ts +49 -0
  409. package/src/spatial-scene/authoring.ts +27 -0
  410. package/src/spatial-scene/camera-track.ts +83 -0
  411. package/src/spatial-scene/contracts.ts +277 -0
  412. package/src/spatial-scene/evaluate.ts +145 -0
  413. package/src/spatial-scene/gltf.ts +558 -0
  414. package/src/spatial-scene/identity.ts +260 -0
  415. package/src/spatial-scene/index.ts +10 -0
  416. package/src/spatial-scene/inspect.ts +68 -0
  417. package/src/spatial-scene/math.ts +280 -0
  418. package/src/spatial-scene/patch.ts +131 -0
  419. package/src/spatial-scene/time.ts +53 -0
  420. package/src/studio/contracts.ts +129 -0
  421. package/src/studio/index.ts +2 -0
  422. package/src/studio/plan.ts +156 -0
  423. package/src/studio/shared.ts +41 -0
  424. package/src/theme.ts +68 -0
  425. package/src/tldr.ts +474 -0
  426. package/src/types.ts +212 -0
  427. package/src/vectorize/archive.ts +101 -0
  428. package/src/vectorize/command.ts +797 -0
  429. package/src/vectorize/index.ts +27 -0
  430. package/src/vectorize/limits.ts +61 -0
  431. package/src/vectorize/metrics.ts +351 -0
  432. package/src/vectorize/pixels.ts +323 -0
  433. package/src/vectorize/supervisor.ts +339 -0
  434. package/src/vectorize/svg.ts +188 -0
  435. package/src/vectorize/tool.ts +542 -0
  436. package/src/vectorize/types.ts +113 -0
  437. package/src/vectorize/vectorize.ts +634 -0
  438. package/src/vectorize/worker-protocol.ts +40 -0
  439. package/src/vectorize/worker.ts +209 -0
  440. package/src/version.ts +1 -0
  441. package/src/workflow.ts +409 -0
@@ -0,0 +1,97 @@
1
+ import { z } from "zod";
2
+ import { createBoundedJsonValueSnapshot } from "../../../../src/code/json-snapshot";
3
+ import {
4
+ EvaluatedSpatialSceneSchema, SpatialAssetManifestSchema, SpatialCameraSchema,
5
+ SpatialDigestSchema, SpatialEntityIdSchema, SpatialGeneratorSchema,
6
+ SpatialOriginSchema, SpatialScenePatchV1Schema, SpatialPlacementSchema, SpatialPoseSchema,
7
+ SpatialSceneV1Schema, SpatialTimeUsSchema,
8
+ } from "../../../../src/spatial-scene/contracts";
9
+ import { applySpatialScenePatch, evaluateSpatialScene, inspectSpatialScene } from "../../../../src/spatial-scene/index";
10
+ import type { OperationDefinition, OperationPolicy } from "../operation";
11
+ import { throwIfAborted } from "./shared";
12
+
13
+ const boundedVector = z.tuple([z.number().finite(), z.number().finite(), z.number().finite()]);
14
+ export const SpatialSceneInspectionOutputSchema = z.strictObject({
15
+ sceneId: z.string(), sceneSha256: SpatialDigestSchema, durationUs: SpatialTimeUsSchema,
16
+ entities: z.array(z.strictObject({
17
+ entityId: SpatialEntityIdSchema, name: z.string(),
18
+ kind: z.enum(["group", "mesh", "image", "diagram", "video", "text", "light", "splat"]),
19
+ origin: SpatialOriginSchema, parentId: SpatialEntityIdSchema.nullable(), placement: SpatialPlacementSchema,
20
+ editableControls: z.array(z.enum(["color", "opacity", "transform"])),
21
+ animatedProperties: z.array(z.string()), assetIds: z.array(z.string()),
22
+ bounds: z.discriminatedUnion("status", [
23
+ z.strictObject({ status: z.literal("authored-enclosure"), coordinateDomain: SpatialPlacementSchema,
24
+ atTimeUs: z.literal(0), bounds: z.strictObject({ min: boundedVector, max: boundedVector }) }),
25
+ z.strictObject({ status: z.literal("unknown"), reason: z.enum(["requires-asset-decoding", "requires-text-layout", "no-surface"]) }),
26
+ ]),
27
+ })).max(4096),
28
+ cameras: z.array(SpatialCameraSchema).max(64),
29
+ assets: z.array(z.strictObject({ assetId: z.string(), manifestSha256: SpatialDigestSchema, manifest: SpatialAssetManifestSchema })).max(128),
30
+ generators: z.array(SpatialGeneratorSchema),
31
+ });
32
+ const capture = (value: unknown) => createBoundedJsonValueSnapshot(value, 4 * 1024 * 1024, "scene operation", { maximumDepth: 36, maximumValues: 400_000 }).value;
33
+ export const SpatialInspectInputSchema = z.preprocess(capture, z.strictObject({ scene: SpatialSceneV1Schema }));
34
+ export const SpatialPatchInputSchema = z.preprocess(capture, z.strictObject({ scene: SpatialSceneV1Schema, patch: SpatialScenePatchV1Schema }));
35
+ export const SpatialPatchOutputSchema = z.strictObject({
36
+ scene: SpatialSceneV1Schema, sceneSha256: SpatialDigestSchema,
37
+ diff: z.array(z.strictObject({
38
+ kind: z.enum(["added", "removed", "changed"]),
39
+ collection: z.enum(["entities", "cameras", "animations", "generators", "overrides", "assets"]),
40
+ id: z.string(), properties: z.array(z.string()),
41
+ })),
42
+ });
43
+ export const SpatialEvaluateInputSchema = z.preprocess(capture, z.strictObject({
44
+ scene: SpatialSceneV1Schema, timeUs: SpatialTimeUsSchema, cameraId: z.string(),
45
+ cameraPoseOverride: SpatialPoseSchema.optional(),
46
+ }));
47
+ export type SpatialInspectInput = z.infer<typeof SpatialInspectInputSchema>;
48
+ export type SpatialInspectOutput = z.infer<typeof SpatialSceneInspectionOutputSchema>;
49
+ export type SpatialPatchInput = z.infer<typeof SpatialPatchInputSchema>;
50
+ export type SpatialPatchOutput = z.infer<typeof SpatialPatchOutputSchema>;
51
+ export type SpatialEvaluateInput = z.infer<typeof SpatialEvaluateInputSchema>;
52
+ export type SpatialEvaluateOutput = z.infer<typeof EvaluatedSpatialSceneSchema>;
53
+
54
+ const purePolicy = Object.freeze({
55
+ cache: "content-addressed", cancellable: true, effect: "pure", maxDurationMs: 10_000,
56
+ maxFanOut: 0, maxInputBytes: 4 * 1024 * 1024, maxOutputBytes: 8 * 1024 * 1024,
57
+ preparation: [], resources: [{ amount: 1, resource: "cpu" }], resume: "deterministic",
58
+ } as const) satisfies OperationPolicy;
59
+
60
+ export const spatialInspectOperationDefinition = {
61
+ kind: "scene.inspect", version: 1,
62
+ inputSchema: SpatialInspectInputSchema, inputSchemaId: "slopcamera.operation.scene.inspect.input/v1",
63
+ outputSchema: SpatialSceneInspectionOutputSchema, outputSchemaId: "slopcamera.operation.scene.inspect.output/v1",
64
+ policy: purePolicy,
65
+ lifecycle: { kind: "pure", execute: (context, input) => {
66
+ throwIfAborted(context.abortSignal);
67
+ return Promise.resolve(SpatialSceneInspectionOutputSchema.parse(inspectSpatialScene(input.scene)));
68
+ } },
69
+ summarize: output => ({ kind: "scene.inspect", fields: { sceneSha256: output.sceneSha256, entities: output.entities.length, cameras: output.cameras.length } }),
70
+ } satisfies OperationDefinition<"scene.inspect", SpatialInspectInput, SpatialInspectOutput>;
71
+
72
+ export const spatialPatchOperationDefinition = {
73
+ kind: "scene.patch", version: 1,
74
+ inputSchema: SpatialPatchInputSchema, inputSchemaId: "slopcamera.operation.scene.patch.input/v1",
75
+ outputSchema: SpatialPatchOutputSchema, outputSchemaId: "slopcamera.operation.scene.patch.output/v1",
76
+ policy: purePolicy,
77
+ lifecycle: { kind: "pure", execute: (context, input) => {
78
+ throwIfAborted(context.abortSignal);
79
+ return Promise.resolve(SpatialPatchOutputSchema.parse(applySpatialScenePatch(input.scene, input.patch)));
80
+ } },
81
+ summarize: output => ({ kind: "scene.patch", fields: { sceneSha256: output.sceneSha256, changes: output.diff.length } }),
82
+ } satisfies OperationDefinition<"scene.patch", SpatialPatchInput, SpatialPatchOutput>;
83
+
84
+ export const spatialEvaluateOperationDefinition = {
85
+ kind: "scene.evaluate", version: 1,
86
+ inputSchema: SpatialEvaluateInputSchema, inputSchemaId: "slopcamera.operation.scene.evaluate.input/v1",
87
+ outputSchema: EvaluatedSpatialSceneSchema, outputSchemaId: "slopcamera.operation.scene.evaluate.output/v1",
88
+ policy: purePolicy,
89
+ lifecycle: { kind: "pure", execute: (context, input) => {
90
+ throwIfAborted(context.abortSignal);
91
+ return Promise.resolve(EvaluatedSpatialSceneSchema.parse(evaluateSpatialScene(input.scene, {
92
+ timeUs: input.timeUs, cameraId: input.cameraId,
93
+ ...(input.cameraPoseOverride === undefined ? {} : { cameraPoseOverride: input.cameraPoseOverride }),
94
+ })));
95
+ } },
96
+ summarize: output => ({ kind: "scene.evaluate", fields: { sceneSha256: output.sceneSha256, viewSha256: output.viewSha256, timeUs: output.timeUs } }),
97
+ } satisfies OperationDefinition<"scene.evaluate", SpatialEvaluateInput, SpatialEvaluateOutput>;
@@ -0,0 +1,143 @@
1
+ import { posix } from "node:path";
2
+
3
+ import { deepFreezeJson } from "../../../../src/code/json-snapshot";
4
+ import { validateStudioReceipt } from "../../../../src/studio";
5
+ import { canonicalJson } from "../../core/canonical-json";
6
+ import type { ApplicationContext } from "../context";
7
+ import { ApplicationError } from "../errors";
8
+ import type { OperationDefinition, OperationExecutionContext } from "../operation";
9
+ import { writeOperationCompletionCheckpoint } from "../operation-completion-checkpoint";
10
+ import {
11
+ BoundStudioRunInputSchema, StudioRunInputSchema, StudioRunOutputSchema,
12
+ type BoundStudioRunInput, type StudioHostControl, type StudioReconciliation, type StudioRunInput, type StudioRunOutput,
13
+ } from "../studio-port";
14
+ import { throwIfAborted } from "./shared";
15
+
16
+ const definitionIdentity = {
17
+ kind: "slopcamera.studio.run", version: 1,
18
+ inputSchemaId: "slopcamera.operation.studio.run.input/v1", outputSchemaId: "slopcamera.operation.studio.run.output/v1",
19
+ } as const;
20
+
21
+ function requirePort(application: ApplicationContext) {
22
+ if (application.studioPort === undefined) throw new ApplicationError("unavailable", "This host has no trusted studio adapter.");
23
+ return application.studioPort;
24
+ }
25
+
26
+ function boundInput(value: unknown): BoundStudioRunInput {
27
+ const input = BoundStudioRunInputSchema.parse(value);
28
+ if (canonicalJson(input.job) !== canonicalJson(input.plan.job)) throw new ApplicationError("conflict", "Studio request job disagrees with its exact plan.");
29
+ if (input.plan.runtime === undefined) throw new ApplicationError("unavailable", "Studio execution requires an observed runtime binding.");
30
+ return deepFreezeJson(input);
31
+ }
32
+
33
+ export function studioAuthorizationRequest(value: unknown) {
34
+ const input = boundInput(value);
35
+ return Object.freeze({ planSha256: input.plan.planSha256, bundleSha256: input.plan.bundleSha256, jobId: input.job.jobId });
36
+ }
37
+
38
+ /** Manifest closure and fixed runtime probing are inert with respect to authored source. */
39
+ export async function bindStudioRunInput(application: ApplicationContext, value: unknown, signal = new AbortController().signal, beforePublication?: () => Promise<void>): Promise<BoundStudioRunInput> {
40
+ throwIfAborted(signal);
41
+ const input = deepFreezeJson(StudioRunInputSchema.parse(value));
42
+ const guard = async () => { throwIfAborted(signal); await application.hostResourceLease?.assertOwned(); await beforePublication?.(); throwIfAborted(signal); };
43
+ await guard();
44
+ const bound = boundInput(await requirePort(application).bind(input, signal, { inheritedFileDescriptors: application.hostResourceLease?.inheritedFileDescriptors ?? [], beforePublication: guard }));
45
+ throwIfAborted(signal);
46
+ if (canonicalJson(input.job) !== canonicalJson(bound.job) || input.bundle.path !== bound.bundle.path
47
+ || ("sha256" in input.bundle && canonicalJson(input.bundle) !== canonicalJson(bound.bundle))
48
+ || (input.plan !== undefined && canonicalJson(input.plan) !== canonicalJson(bound.plan))) {
49
+ throw new ApplicationError("conflict", "Studio source, job, or runtime changed after planning.");
50
+ }
51
+ return bound;
52
+ }
53
+
54
+ /** The host verifies physical bytes; this boundary verifies their complete typed projection. */
55
+ export function validateStudioRunOutput(inputValue: unknown, outputValue: unknown): StudioRunOutput {
56
+ const input = boundInput(inputValue);
57
+ const output = StudioRunOutputSchema.parse(outputValue);
58
+ const receipt = validateStudioReceipt({ plan: input.plan, receipt: output.document });
59
+ const expected = new Map(receipt.outputs.map(item => [posix.join(posix.dirname(output.receipt.path), "outputs", item.path), item]));
60
+ if (expected.size !== output.outputs.length) throw new ApplicationError("invalid-data", "Studio output references must cover every retained receipt artifact exactly once.");
61
+ for (const item of output.outputs) {
62
+ const artifact = expected.get(item.artifact.path);
63
+ if (artifact === undefined || artifact.outputId !== item.outputId || artifact.sha256 !== item.artifact.sha256
64
+ || artifact.bytes !== item.artifact.bytes || artifact.role !== item.role || artifact.format !== item.format) {
65
+ throw new ApplicationError("invalid-data", "Studio output reference disagrees with its retained receipt artifact.");
66
+ }
67
+ expected.delete(item.artifact.path);
68
+ }
69
+ return deepFreezeJson(output);
70
+ }
71
+
72
+ function controlFor(context: OperationExecutionContext): StudioHostControl {
73
+ const workflow = context.workflow;
74
+ if (workflow === undefined) throw new ApplicationError("unavailable", "Studio workflow execution requires an exact durable node workspace.");
75
+ return {
76
+ signal: context.abortSignal,
77
+ workspaceDirectory: workflow.workspaceDirectory,
78
+ identity: { runId: workflow.runId, nodeKey: workflow.nodeKey, nodePlanSha256: workflow.nodePlanSha256 },
79
+ inheritedFileDescriptors: context.application.hostResourceLease?.inheritedFileDescriptors ?? [],
80
+ beforePublication: async () => {
81
+ throwIfAborted(context.abortSignal);
82
+ await context.application.hostResourceLease?.assertOwned();
83
+ await workflow.beforePublication();
84
+ throwIfAborted(context.abortSignal);
85
+ },
86
+ };
87
+ }
88
+
89
+ function assertSucceeded(output: StudioRunOutput): void {
90
+ if (output.document.state === "succeeded") return;
91
+ throw new ApplicationError(output.document.state === "unknown-custody" ? "ambiguous" : "subprocess",
92
+ output.document.state === "unknown-custody" ? "Studio native process custody is unresolved; inspect the retained receipt before further execution." : "Studio job failed; inspect its retained receipt.",
93
+ { receipt: output.receipt, state: output.document.state });
94
+ }
95
+
96
+ export async function executeStudioRun(context: OperationExecutionContext, value: StudioRunInput): Promise<StudioRunOutput> {
97
+ const control = controlFor(context);
98
+ const original = boundInput(value);
99
+ const input = await bindStudioRunInput(context.application, original, context.abortSignal, control.beforePublication);
100
+ if (input.plan.readiness !== "authorization-required") throw new ApplicationError("unavailable", "Studio runtime has unavailable or unverified required capabilities.");
101
+ const authorization = context.application.studioAuthorization;
102
+ if (authorization === undefined || !await authorization.authorize(studioAuthorizationRequest(input))) {
103
+ throw new ApplicationError("authorization-required", "This exact studio plan requires explicit trusted-current-user code authorization from the host.");
104
+ }
105
+ await control.beforePublication();
106
+ const output = validateStudioRunOutput(input, await requirePort(context.application).execute(input, { ...control, allowTrustedCode: true }));
107
+ assertSucceeded(output);
108
+ await control.beforePublication();
109
+ await writeOperationCompletionCheckpoint(context, definitionIdentity, output);
110
+ return output;
111
+ }
112
+
113
+ /** Reconciliation never invokes authored source and never returns permission to retry. */
114
+ export async function reconcileStudioRun(context: OperationExecutionContext, exactInput: unknown, checkpointOutput?: unknown): Promise<StudioReconciliation> {
115
+ const input = boundInput(exactInput);
116
+ const control = controlFor(context);
117
+ throwIfAborted(control.signal);
118
+ const recovered = await requirePort(context.application).reconcile(input, control);
119
+ throwIfAborted(control.signal);
120
+ if (recovered.kind !== "completed") return recovered;
121
+ const output = validateStudioRunOutput(input, recovered.output);
122
+ if (output.document.state !== "succeeded") return { kind: "ambiguous", message: "Studio retained job did not complete successfully; no automatic execution is allowed." };
123
+ if (checkpointOutput !== undefined && canonicalJson(StudioRunOutputSchema.parse(checkpointOutput)) !== canonicalJson(output)) {
124
+ return { kind: "incompatible", message: "Studio checkpoint disagrees with the authoritative native job receipt." };
125
+ }
126
+ return { kind: "completed", output };
127
+ }
128
+
129
+ export const studioRunOperationDefinition = {
130
+ ...definitionIdentity,
131
+ inputSchema: StudioRunInputSchema, outputSchema: StudioRunOutputSchema,
132
+ lifecycle: { kind: "local-artifact", execute: executeStudioRun },
133
+ policy: {
134
+ cache: "exact-run", cancellable: true, effect: "local-derived-write", maxDurationMs: 6 * 60 * 60 * 1000,
135
+ maxFanOut: 0, maxInputBytes: 64 * 1024 * 1024, maxOutputBytes: 64 * 1024 * 1024,
136
+ preparation: ["local-media"], resources: [
137
+ { amount: 1, resource: "cpu" }, { amount: 1, resource: "local-io" },
138
+ { amount: 1, resource: "ffmpeg" }, { amount: 1, resource: "output-publication" },
139
+ ], resume: "verified-receipt",
140
+ },
141
+ receiptReference: output => output.receipt.path,
142
+ summarize: output => ({ kind: "slopcamera.studio.run", fields: { jobId: output.document.jobId, planSha256: output.document.planSha256, state: output.document.state, outputs: output.outputs.length } }),
143
+ } satisfies OperationDefinition<"slopcamera.studio.run", StudioRunInput, StudioRunOutput>;
@@ -0,0 +1,163 @@
1
+ import {
2
+ lstat,
3
+ mkdir,
4
+ realpath,
5
+ } from "node:fs/promises";
6
+ import { join } from "node:path";
7
+ import { Effect } from "effect";
8
+
9
+ import {
10
+ RepositoryRelativePathSchema,
11
+ VideoProjectIdSchema,
12
+ } from "../contracts";
13
+ import { sha256Hex } from "../core/canonical-json";
14
+ import { acquireMutationLease, type MutationLease } from "../cli/mutation-lock";
15
+ import type { ApplicationContext } from "./context";
16
+ import { ApplicationError } from "./errors";
17
+ import { operationBoundary, operationFinally, operationResource, type OperationEffectFailure } from "./operation-effects";
18
+
19
+ const OUTPUT_LEASE_ROOT = "output-publication-leases";
20
+ const inProcessOutputTails = new Map<string, Promise<void>>();
21
+
22
+ export interface OutputPublicationTarget {
23
+ readonly outputPath: string;
24
+ readonly projectId: string;
25
+ }
26
+
27
+ async function privatePhysicalDirectory(
28
+ path: string,
29
+ create: boolean,
30
+ ): Promise<string> {
31
+ if (create) await mkdir(path, { mode: 0o700, recursive: true });
32
+ const details = await lstat(path);
33
+ if (
34
+ details.isSymbolicLink()
35
+ || !details.isDirectory()
36
+ || (details.mode & 0o077) !== 0
37
+ ) {
38
+ throw new ApplicationError(
39
+ "unsafe-path",
40
+ `Output publication lease state must be a private physical directory: ${path}`,
41
+ );
42
+ }
43
+ return await realpath(path);
44
+ }
45
+
46
+ async function leaseDirectory(
47
+ application: ApplicationContext,
48
+ target: OutputPublicationTarget,
49
+ ): Promise<{
50
+ readonly directory: string;
51
+ readonly outputPath: string;
52
+ readonly projectId: string;
53
+ }> {
54
+ const projectId = VideoProjectIdSchema.parse(target.projectId);
55
+ const outputPath = RepositoryRelativePathSchema.parse(target.outputPath);
56
+ if (!outputPath.startsWith("renders/")) {
57
+ throw new ApplicationError(
58
+ "unsafe-path",
59
+ "Workflow render outputs must remain beneath the project renders directory.",
60
+ );
61
+ }
62
+ const privateRoot = await privatePhysicalDirectory(
63
+ application.paths.privateRoot,
64
+ true,
65
+ );
66
+ const rootPath = join(privateRoot, OUTPUT_LEASE_ROOT);
67
+ await mkdir(rootPath, { mode: 0o700, recursive: true });
68
+ const root = await privatePhysicalDirectory(rootPath, false);
69
+ const key = sha256Hex(
70
+ `studio.output-publication-lease/v1\0${projectId}\0${outputPath}`,
71
+ );
72
+ const requestedDirectory = join(root, key);
73
+ await mkdir(requestedDirectory, { mode: 0o700, recursive: true });
74
+ return {
75
+ directory: await privatePhysicalDirectory(requestedDirectory, false),
76
+ outputPath,
77
+ projectId,
78
+ };
79
+ }
80
+
81
+ function reserveOutputTurn(key: string): { readonly ready: Promise<void>; release(): void } {
82
+ const previous = inProcessOutputTails.get(key) ?? Promise.resolve();
83
+ const { promise: settled, resolve: complete } = Promise.withResolvers<void>();
84
+ inProcessOutputTails.set(key, settled);
85
+ let released = false;
86
+ return {
87
+ ready: previous,
88
+ release: () => {
89
+ if (released) return;
90
+ released = true;
91
+ if (inProcessOutputTails.get(key) === settled) inProcessOutputTails.delete(key);
92
+ complete();
93
+ },
94
+ };
95
+ }
96
+
97
+ interface OutputLease extends MutationLease {
98
+ releaseTurn(): void;
99
+ }
100
+
101
+ async function acquireOutputLease(
102
+ application: ApplicationContext,
103
+ target: OutputPublicationTarget,
104
+ ): Promise<OutputLease> {
105
+ const targetLease = await leaseDirectory(application, target);
106
+ // Promise and native Effect callers reserve in the same physical-key queue.
107
+ const turn = reserveOutputTurn(targetLease.directory);
108
+ await turn.ready;
109
+ try {
110
+ const lease = await acquireMutationLease(targetLease.directory, {
111
+ command: "workflow:render.project",
112
+ label: `${targetLease.projectId}/${targetLease.outputPath}`,
113
+ now: application.clock.now,
114
+ });
115
+ return { ...lease, releaseTurn: turn.release };
116
+ } catch (error) {
117
+ turn.release();
118
+ throw error;
119
+ }
120
+ }
121
+
122
+ export function withOutputPublicationLeaseEffect<Value, R>(
123
+ application: ApplicationContext,
124
+ target: OutputPublicationTarget,
125
+ use: Effect.Effect<Value, OperationEffectFailure, R>,
126
+ ): Effect.Effect<Value, OperationEffectFailure, R> {
127
+ return operationResource(
128
+ operationBoundary("publication", () => acquireOutputLease(application, target)),
129
+ () => use,
130
+ lease => operationFinally(
131
+ operationFinally(
132
+ operationBoundary("cleanup", () => lease.close()),
133
+ operationBoundary("cleanup", () => lease.unlinkIfOwned()),
134
+ ),
135
+ Effect.sync(() => lease.releaseTurn()),
136
+ ),
137
+ );
138
+ }
139
+
140
+ /**
141
+ * Serializes only publication of one exact project output. Different output
142
+ * paths retain scheduler and process-level concurrency.
143
+ */
144
+ export async function withOutputPublicationLease<Value>(
145
+ application: ApplicationContext,
146
+ target: OutputPublicationTarget,
147
+ execute: () => Promise<Value>,
148
+ ): Promise<Value> {
149
+ const lease = await acquireOutputLease(application, target);
150
+ try {
151
+ return await execute();
152
+ } finally {
153
+ try {
154
+ await lease.close();
155
+ } finally {
156
+ try {
157
+ await lease.unlinkIfOwned();
158
+ } finally {
159
+ lease.releaseTurn();
160
+ }
161
+ }
162
+ }
163
+ }
@@ -0,0 +1,233 @@
1
+ import type { ApplicationContext } from "./context";
2
+ import { ApplicationError } from "./errors";
3
+ import type { OperationKind } from "./operation";
4
+ import {
5
+ ProjectAnalysisReferenceSchema,
6
+ VideoProjectV1Schema,
7
+ type VideoProjectV1,
8
+ } from "../contracts";
9
+ import { canonicalJson } from "../core/canonical-json";
10
+ import {
11
+ canonicalSlopcameraPersistenceDocument,
12
+ createNodeBundleFileSystem,
13
+ saveVideoProject,
14
+ } from "../core/storage";
15
+ import { withMutationLock } from "../cli/mutation-lock";
16
+ import {
17
+ recoverProjectStateTransaction,
18
+ } from "../cli/project-state-transaction";
19
+ import { resolveProjectDirectory } from "../cli/project-service";
20
+ import {
21
+ assertProjectEditBasis,
22
+ openProjectSnapshot,
23
+ type ProjectEditBasis,
24
+ type OpenProjectSnapshot,
25
+ } from "./project-store";
26
+
27
+ const inProcessPublicationTails = new Map<string, Promise<void>>();
28
+
29
+ async function withInProcessPublicationQueue<Value>(
30
+ key: string,
31
+ execute: () => Promise<Value>,
32
+ ): Promise<Value> {
33
+ const previous = inProcessPublicationTails.get(key) ?? Promise.resolve();
34
+ const result = previous.then(execute);
35
+ const settled = result.then(
36
+ () => undefined,
37
+ () => undefined,
38
+ );
39
+ inProcessPublicationTails.set(key, settled);
40
+ try {
41
+ return await result;
42
+ } finally {
43
+ if (inProcessPublicationTails.get(key) === settled) {
44
+ inProcessPublicationTails.delete(key);
45
+ }
46
+ }
47
+ }
48
+
49
+ function projectReference(input: unknown): string {
50
+ if (typeof input !== "object" || input === null || Array.isArray(input)) {
51
+ throw new ApplicationError(
52
+ "invalid-data",
53
+ "A project publication operation requires an object input.",
54
+ );
55
+ }
56
+ const project = (input as Readonly<Record<string, unknown>>).project;
57
+ if (typeof project !== "string") {
58
+ throw new ApplicationError(
59
+ "invalid-data",
60
+ "A project publication operation requires a project reference.",
61
+ );
62
+ }
63
+ return project;
64
+ }
65
+
66
+ async function withResolvedProjectMutationLease<Value>(
67
+ application: ApplicationContext,
68
+ reference: string,
69
+ command: string,
70
+ execute: (
71
+ directory: Awaited<ReturnType<typeof resolveProjectDirectory>>,
72
+ ) => Promise<Value>,
73
+ ): Promise<Value> {
74
+ const directory = await resolveProjectDirectory(
75
+ application.paths.projectRoot,
76
+ reference,
77
+ );
78
+ return await withInProcessPublicationQueue(directory.path, async () =>
79
+ await withMutationLock(directory.path, {
80
+ command,
81
+ label: `project ${directory.id}`,
82
+ now: application.clock.now,
83
+ }, async () => await execute(directory)));
84
+ }
85
+
86
+ /**
87
+ * Bind one immutable project/plan generation while sharing the same brief
88
+ * in-process and physical mutation lease as project publication. The lease is
89
+ * released before any analysis work begins. Reads fail closed on an unsettled
90
+ * transaction; only mutation owners may recover one.
91
+ */
92
+ export async function openLeasedProjectSnapshot(
93
+ application: ApplicationContext,
94
+ reference: string,
95
+ ): Promise<OpenProjectSnapshot> {
96
+ return await withResolvedProjectMutationLease(
97
+ application,
98
+ reference,
99
+ "workflow:project.snapshot:read",
100
+ async directory => await openProjectSnapshot(
101
+ application.paths.projectRoot,
102
+ directory.id,
103
+ ),
104
+ );
105
+ }
106
+
107
+ /** Serialize only the checked publication phase with existing CLI mutations. */
108
+ export async function withProjectPublicationLease<Value>(
109
+ application: ApplicationContext,
110
+ operation: OperationKind,
111
+ input: unknown,
112
+ execute: () => Promise<Value>,
113
+ beforeRecoveryMutation?: () => Promise<void>,
114
+ ): Promise<Value> {
115
+ const reference = projectReference(input);
116
+ return await withResolvedProjectMutationLease(
117
+ application,
118
+ reference,
119
+ `workflow:${operation}`,
120
+ async directory => {
121
+ // Transaction recovery may itself restore project or plan bytes. A
122
+ // reconciliation caller therefore fences after acquiring the physical
123
+ // lock and immediately before allowing that recovery write boundary.
124
+ await beforeRecoveryMutation?.();
125
+ await recoverProjectStateTransaction(createNodeBundleFileSystem(
126
+ directory.path,
127
+ ));
128
+ return await execute();
129
+ },
130
+ );
131
+ }
132
+
133
+ type ProjectAnalysisReference = ReturnType<
134
+ typeof ProjectAnalysisReferenceSchema.parse
135
+ >;
136
+
137
+ export function projectAnalysisPublicationBasis(
138
+ snapshot: OpenProjectSnapshot,
139
+ ): ProjectEditBasis {
140
+ return snapshot.editBasis;
141
+ }
142
+
143
+ export interface MergeProjectAnalysisReferenceOptions {
144
+ readonly application: ApplicationContext;
145
+ /**
146
+ * The original edit basis that authorized the analysis. Its prefix
147
+ * commitment deliberately permits later sibling analyses while rejecting
148
+ * removal, reordering, or replacement of any prior authority.
149
+ */
150
+ readonly basis: ProjectEditBasis;
151
+ /**
152
+ * Revalidates a durable workflow fence after waiting for the physical
153
+ * publication lock and immediately before mutating the project document.
154
+ */
155
+ readonly beforePublication?: () => Promise<void>;
156
+ readonly operation: Extract<OperationKind, `analysis.${string}`>;
157
+ readonly project: string;
158
+ readonly reference: ProjectAnalysisReference;
159
+ }
160
+
161
+ export async function mergeProjectAnalysisReference(
162
+ options: MergeProjectAnalysisReferenceOptions,
163
+ ): Promise<{
164
+ readonly project: VideoProjectV1;
165
+ readonly reference: ProjectAnalysisReference;
166
+ }> {
167
+ const reference = ProjectAnalysisReferenceSchema.parse(options.reference);
168
+ if (Date.parse(reference.createdAt) < Date.parse(options.basis.projectUpdatedAt)) {
169
+ throw new ApplicationError(
170
+ "conflict",
171
+ "The analysis update time cannot precede the project snapshot update time.",
172
+ );
173
+ }
174
+ return await withProjectPublicationLease(
175
+ options.application,
176
+ options.operation,
177
+ { project: options.project },
178
+ async () => {
179
+ await options.beforePublication?.();
180
+ const latest = await openProjectSnapshot(
181
+ options.application.paths.projectRoot,
182
+ options.project,
183
+ );
184
+ assertProjectEditBasis(options.basis, latest);
185
+ const existing = latest.project.analyses.find(
186
+ candidate => candidate.analysisId === reference.analysisId,
187
+ );
188
+ if (existing !== undefined) {
189
+ if (canonicalJson(existing) !== canonicalJson(reference)) {
190
+ throw new ApplicationError(
191
+ "conflict",
192
+ `Project analysis identity was published with different evidence: ${reference.analysisId}`,
193
+ );
194
+ }
195
+ return { project: latest.project, reference };
196
+ }
197
+ const publicationSnapshot = await openProjectSnapshot(
198
+ options.application.paths.projectRoot,
199
+ options.project,
200
+ );
201
+ // Reassert the original append-only basis after the final durable
202
+ // workflow fence. This both preserves sibling analyses that arrived
203
+ // during recovery and makes the check the last fallible I/O before save.
204
+ assertProjectEditBasis(options.basis, publicationSnapshot);
205
+ const publicationExisting = publicationSnapshot.project.analyses.find(
206
+ candidate => candidate.analysisId === reference.analysisId,
207
+ );
208
+ if (publicationExisting !== undefined) {
209
+ if (canonicalJson(publicationExisting) !== canonicalJson(reference)) {
210
+ throw new ApplicationError(
211
+ "conflict",
212
+ `Project analysis identity was published with different evidence: ${reference.analysisId}`,
213
+ );
214
+ }
215
+ return { project: publicationSnapshot.project, reference };
216
+ }
217
+ const updatedAt = Date.parse(reference.createdAt)
218
+ >= Date.parse(publicationSnapshot.project.updatedAt)
219
+ ? reference.createdAt
220
+ : publicationSnapshot.project.updatedAt;
221
+ const project = canonicalSlopcameraPersistenceDocument(VideoProjectV1Schema.parse({
222
+ ...publicationSnapshot.project,
223
+ analyses: [...publicationSnapshot.project.analyses, reference],
224
+ updatedAt,
225
+ }));
226
+ await options.beforePublication?.();
227
+ assertProjectEditBasis(options.basis, publicationSnapshot);
228
+ await saveVideoProject(publicationSnapshot.openProject.fileSystem, project);
229
+ return { project, reference };
230
+ },
231
+ options.beforePublication,
232
+ );
233
+ }