@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,112 @@
1
+ import { z } from "zod";
2
+
3
+ import {
4
+ CameraMoveIdSchema,
5
+ FaceTrackIdSchema,
6
+ Sha256Schema,
7
+ VideoProjectIdSchema,
8
+ type ReadonlyInferred,
9
+ } from "../contracts";
10
+
11
+ const AgentCommandSchema = z.string()
12
+ .min(1)
13
+ .max(512)
14
+ .refine(value => !/[\r\n]/u.test(value), "Next command must fit on one line.");
15
+
16
+ type CameraMoveId = z.infer<typeof CameraMoveIdSchema>;
17
+ type VideoProjectId = z.infer<typeof VideoProjectIdSchema>;
18
+
19
+ export function projectCameraNextCommands(
20
+ projectId: VideoProjectId,
21
+ cameraMoveId: CameraMoveId,
22
+ ): Readonly<{ readonly remove: string; readonly show: string }> {
23
+ return {
24
+ remove: `slopcamera project edit ${projectId} camera remove ${cameraMoveId} --json`,
25
+ show: `slopcamera project edit ${projectId} camera show --json`,
26
+ };
27
+ }
28
+
29
+ export const ProjectCameraSelectionReceiptSchema = z.strictObject({
30
+ kind: z.enum(["all", "explicit", "largest"]),
31
+ requireAllSelected: z.boolean(),
32
+ trackIds: z.array(FaceTrackIdSchema).min(1).max(64),
33
+ }).superRefine((selection, context) => {
34
+ if (new Set(selection.trackIds).size !== selection.trackIds.length) {
35
+ context.addIssue({ code: "custom", message: "Camera receipt track IDs must be unique." });
36
+ }
37
+ if (selection.kind === "largest" && selection.requireAllSelected) {
38
+ context.addIssue({
39
+ code: "custom",
40
+ message: "Dynamic largest-visible selection cannot require every candidate track.",
41
+ });
42
+ }
43
+ });
44
+
45
+ export const ProjectCameraCreateReceiptSchema = z.strictObject({
46
+ cameraMoveId: CameraMoveIdSchema,
47
+ cameraMoves: z.number().int().safe().positive().max(4_096),
48
+ keyframeCount: z.number().int().safe().min(2).max(4_096),
49
+ nextCommands: z.strictObject({
50
+ remove: AgentCommandSchema,
51
+ show: AgentCommandSchema,
52
+ }),
53
+ operation: z.enum(["follow-faces", "path", "push", "reframe"]),
54
+ planHash: Sha256Schema,
55
+ projectId: VideoProjectIdSchema,
56
+ selection: ProjectCameraSelectionReceiptSchema.nullable(),
57
+ }).superRefine((receipt, context) => {
58
+ if ((receipt.operation === "follow-faces") !== (receipt.selection !== null)) {
59
+ context.addIssue({
60
+ code: "custom",
61
+ message: "Only face-follow camera receipts carry a face selection.",
62
+ });
63
+ }
64
+ const expected = projectCameraNextCommands(receipt.projectId, receipt.cameraMoveId);
65
+ if (receipt.nextCommands.show !== expected.show) {
66
+ context.addIssue({
67
+ code: "custom",
68
+ message: "Camera receipt show command must exactly match its project ID.",
69
+ path: ["nextCommands", "show"],
70
+ });
71
+ }
72
+ if (receipt.nextCommands.remove !== expected.remove) {
73
+ context.addIssue({
74
+ code: "custom",
75
+ message: "Camera receipt remove command must exactly match its project and camera move IDs.",
76
+ path: ["nextCommands", "remove"],
77
+ });
78
+ }
79
+ });
80
+
81
+ export const ProjectCameraRemoveReceiptSchema = z.strictObject({
82
+ cameraMoveId: CameraMoveIdSchema,
83
+ cameraMoves: z.number().int().safe().nonnegative().max(4_096),
84
+ keyframeCount: z.number().int().safe().min(2).max(4_096),
85
+ nextCommands: z.strictObject({
86
+ show: AgentCommandSchema,
87
+ }),
88
+ operation: z.literal("remove"),
89
+ planHash: Sha256Schema,
90
+ projectId: VideoProjectIdSchema,
91
+ }).superRefine((receipt, context) => {
92
+ const expected = projectCameraNextCommands(receipt.projectId, receipt.cameraMoveId);
93
+ if (receipt.nextCommands.show !== expected.show) {
94
+ context.addIssue({
95
+ code: "custom",
96
+ message: "Camera receipt show command must exactly match its project ID.",
97
+ path: ["nextCommands", "show"],
98
+ });
99
+ }
100
+ });
101
+
102
+ export const ProjectCameraEditReceiptSchema = z.union([
103
+ ProjectCameraCreateReceiptSchema,
104
+ ProjectCameraRemoveReceiptSchema,
105
+ ]);
106
+
107
+ export type ProjectCameraSelectionReceipt = ReadonlyInferred<
108
+ typeof ProjectCameraSelectionReceiptSchema
109
+ >;
110
+ export type ProjectCameraEditReceipt = ReadonlyInferred<
111
+ typeof ProjectCameraEditReceiptSchema
112
+ >;
@@ -0,0 +1,394 @@
1
+ import {
2
+ FaceTrackIdSchema,
3
+ ProjectCameraMoveSchema,
4
+ ProjectPlacementIdSchema,
5
+ type Easing,
6
+ type FaceAnalysisV1,
7
+ type FaceTrackId,
8
+ type ProjectCameraMove,
9
+ type ProjectEditPlanV1,
10
+ type VideoProjectV1,
11
+ } from "../contracts";
12
+ import {
13
+ faceCoordinateDisplayAspect,
14
+ mapFaceResultsToPreparedLayer,
15
+ planFaceFraming,
16
+ type PreparedFaceFrame,
17
+ } from "../core/face-framing";
18
+ import {
19
+ hashProjectCameraGeometry,
20
+ hashProjectCameraSync,
21
+ } from "../core/project-camera";
22
+ import {
23
+ assetToProjectUs,
24
+ projectToAssetUs,
25
+ } from "../core/project-time";
26
+ import { CliError } from "./errors";
27
+
28
+ type FaceReference = Extract<
29
+ VideoProjectV1["analyses"][number],
30
+ { readonly kind: "faces" }
31
+ >;
32
+
33
+ export interface ProjectFaceCameraOptions {
34
+ readonly analysis: FaceAnalysisV1;
35
+ readonly cameraMoveId: string;
36
+ readonly easing: Easing;
37
+ readonly framing: "tight" | "medium" | "wide" | "group";
38
+ readonly gapPolicy: "hold" | "fallback" | "fail";
39
+ readonly headroom: number;
40
+ readonly maximumZoom: number;
41
+ readonly minimumZoom: number;
42
+ readonly outputHeight: number;
43
+ readonly outputWidth: number;
44
+ readonly placementId: string;
45
+ readonly plan: ProjectEditPlanV1;
46
+ readonly project: VideoProjectV1;
47
+ readonly projectRange: Readonly<{ readonly endUs: number; readonly startUs: number }>;
48
+ readonly reference: FaceReference;
49
+ readonly requireAllSelectedFaces: boolean;
50
+ readonly selection:
51
+ | Readonly<{ readonly kind: "explicit"; readonly trackIds: readonly string[] }>
52
+ | Readonly<{ readonly kind: "all" | "largest" }>;
53
+ readonly smoothingSeconds: number;
54
+ }
55
+
56
+ export interface PlannedProjectFaceCamera {
57
+ readonly move: ProjectCameraMove;
58
+ readonly selectedTrackIds: readonly FaceTrackId[];
59
+ }
60
+
61
+ function clamp(value: number, minimum: number, maximum: number): number {
62
+ return Math.max(minimum, Math.min(maximum, value));
63
+ }
64
+
65
+ function activeTrackIds(
66
+ analysis: FaceAnalysisV1,
67
+ range: Readonly<{ readonly endUs: number; readonly startUs: number }>,
68
+ ): readonly FaceTrackId[] {
69
+ const ids = new Set<FaceTrackId>();
70
+ for (const result of analysis.results) {
71
+ if (
72
+ result.state !== "analyzed"
73
+ || result.assetTimeUs < range.startUs
74
+ || result.assetTimeUs >= range.endUs
75
+ ) {
76
+ continue;
77
+ }
78
+ for (const detection of result.detections) ids.add(detection.trackId);
79
+ }
80
+ return [...ids].sort((left, right) => left.localeCompare(right));
81
+ }
82
+
83
+ function selectedTrackIds(
84
+ analysis: FaceAnalysisV1,
85
+ range: Readonly<{ readonly endUs: number; readonly startUs: number }>,
86
+ selection: ProjectFaceCameraOptions["selection"],
87
+ ): readonly FaceTrackId[] {
88
+ const known = new Set(analysis.tracks.map(track => track.trackId));
89
+ let selected: readonly FaceTrackId[];
90
+ if (selection.kind === "explicit") {
91
+ selected = selection.trackIds.map((trackId) => {
92
+ const parsed = FaceTrackIdSchema.safeParse(trackId);
93
+ if (!parsed.success) throw new CliError("usage", `Invalid face track ID: ${trackId}`);
94
+ if (!known.has(parsed.data)) {
95
+ throw new CliError("not-found", `Face analysis does not contain track ${trackId}.`);
96
+ }
97
+ return parsed.data;
98
+ });
99
+ } else {
100
+ selected = activeTrackIds(analysis, range);
101
+ }
102
+ selected = [...new Set(selected)].sort((left, right) => left.localeCompare(right));
103
+ if (selected.length === 0) {
104
+ throw new CliError("not-found", "No selected face track has evidence in the requested camera range.");
105
+ }
106
+ if (selected.length > 64) {
107
+ throw new CliError(
108
+ "usage",
109
+ `Face-follow supports at most 64 selected tracks; narrow the selection from ${selected.length}.`,
110
+ );
111
+ }
112
+ return selected;
113
+ }
114
+
115
+ function largestVisibleFaceFrames(
116
+ frames: readonly PreparedFaceFrame[],
117
+ candidateTrackIds: readonly FaceTrackId[],
118
+ range: Readonly<{ readonly endUs: number; readonly startUs: number }>,
119
+ ): Readonly<{
120
+ readonly frames: readonly PreparedFaceFrame[];
121
+ readonly selectedTrackIds: readonly FaceTrackId[];
122
+ }> {
123
+ const candidates = new Set(candidateTrackIds);
124
+ const selected = new Set<FaceTrackId>();
125
+ const filtered = frames.map((frame): PreparedFaceFrame => {
126
+ if (
127
+ frame.state === "failed"
128
+ || frame.assetTimeUs < range.startUs
129
+ || frame.assetTimeUs >= range.endUs
130
+ ) {
131
+ return frame;
132
+ }
133
+ const winner = frame.detections
134
+ .filter(detection => candidates.has(detection.trackId))
135
+ .sort((left, right) => {
136
+ const leftArea = left.rect.width * left.rect.height;
137
+ const rightArea = right.rect.width * right.rect.height;
138
+ if (leftArea !== rightArea) return leftArea < rightArea ? 1 : -1;
139
+ if (left.confidence !== right.confidence) {
140
+ return left.confidence < right.confidence ? 1 : -1;
141
+ }
142
+ if (left.trackId === right.trackId) return 0;
143
+ return left.trackId < right.trackId ? -1 : 1;
144
+ })[0];
145
+ if (winner === undefined) return { ...frame, detections: [] };
146
+ selected.add(winner.trackId);
147
+ return { ...frame, detections: [winner] };
148
+ });
149
+ return {
150
+ frames: filtered,
151
+ selectedTrackIds: [...selected].sort((left, right) => left.localeCompare(right)),
152
+ };
153
+ }
154
+
155
+ function gapPolicy(
156
+ value: ProjectFaceCameraOptions["gapPolicy"],
157
+ maximumGapUs: number,
158
+ ) {
159
+ if (value === "fail") return { kind: "fail" as const };
160
+ if (value === "fallback") return { kind: "fallback" as const };
161
+ return {
162
+ kind: "hold" as const,
163
+ maximumHoldUs: Math.max(maximumGapUs, 1_000_000),
164
+ whenExpired: "fallback" as const,
165
+ };
166
+ }
167
+
168
+ function paddingRatio(framing: ProjectFaceCameraOptions["framing"]): number {
169
+ if (framing === "tight") return 0.15;
170
+ if (framing === "medium") return 0.45;
171
+ if (framing === "wide") return 0.9;
172
+ return 0.3;
173
+ }
174
+
175
+ function layerPixels(
176
+ presentation: Extract<
177
+ VideoProjectV1["placements"][number]["video"][number]["presentation"],
178
+ { readonly enabled: true }
179
+ >,
180
+ outputWidth: number,
181
+ outputHeight: number,
182
+ ): Readonly<{ readonly height: number; readonly width: number }> {
183
+ return presentation.layout.kind === "output-pixels"
184
+ ? {
185
+ height: Math.max(1, Math.round(presentation.layout.height)),
186
+ width: Math.max(1, Math.round(presentation.layout.width)),
187
+ }
188
+ : {
189
+ height: Math.max(1, Math.round(presentation.layout.height * outputHeight)),
190
+ width: Math.max(1, Math.round(presentation.layout.width * outputWidth)),
191
+ };
192
+ }
193
+
194
+ export function planProjectFaceCamera(
195
+ options: ProjectFaceCameraOptions,
196
+ ): PlannedProjectFaceCamera {
197
+ if (
198
+ !Number.isFinite(options.minimumZoom)
199
+ || !Number.isFinite(options.maximumZoom)
200
+ || options.minimumZoom < 1
201
+ || options.maximumZoom < options.minimumZoom
202
+ || options.maximumZoom > 10
203
+ || !Number.isFinite(options.headroom)
204
+ || options.headroom < 0
205
+ || options.headroom > 1
206
+ || !Number.isFinite(options.smoothingSeconds)
207
+ || options.smoothingSeconds < 0
208
+ || options.smoothingSeconds > 60
209
+ ) {
210
+ throw new CliError("usage", "Face-follow framing, zoom, or smoothing values are invalid.");
211
+ }
212
+ if (options.selection.kind === "largest" && options.requireAllSelectedFaces) {
213
+ throw new CliError(
214
+ "usage",
215
+ "Dynamic largest-visible face selection cannot require every candidate track at once.",
216
+ );
217
+ }
218
+ if (
219
+ !Number.isSafeInteger(options.outputWidth)
220
+ || !Number.isSafeInteger(options.outputHeight)
221
+ || options.outputWidth <= 0
222
+ || options.outputHeight <= 0
223
+ || options.outputWidth > 16_384
224
+ || options.outputHeight > 16_384
225
+ || options.outputWidth * options.outputHeight > 134_217_728
226
+ ) {
227
+ throw new CliError("usage", "Face-follow output dimensions are invalid or exceed 128 megapixels.");
228
+ }
229
+ if (options.projectRange.endUs > options.project.timeline.durationUs) {
230
+ throw new CliError("usage", "Face-follow range exceeds the project timeline.");
231
+ }
232
+ const placementId = ProjectPlacementIdSchema.safeParse(options.placementId);
233
+ if (!placementId.success) {
234
+ throw new CliError("usage", `Invalid project placement: ${options.placementId}`);
235
+ }
236
+ const placement = options.project.placements.find(
237
+ candidate => candidate.placementId === placementId.data,
238
+ );
239
+ if (placement === undefined || !placement.enabled) {
240
+ throw new CliError("not-found", `Unknown enabled project placement: ${options.placementId}`);
241
+ }
242
+ if (
243
+ options.reference.analysisId !== options.analysis.analysisId
244
+ || placement.assetId !== options.analysis.subject.assetId
245
+ || options.reference.assetId !== options.analysis.subject.assetId
246
+ || options.reference.streamId !== options.analysis.subject.streamId
247
+ || options.reference.subjectIntegritySha256 !== options.analysis.subject.integritySha256
248
+ ) {
249
+ throw new CliError(
250
+ "conflict",
251
+ "Face analysis reference or subject does not match the selected project placement.",
252
+ );
253
+ }
254
+ const configured = placement.video.find(
255
+ candidate => candidate.streamId === options.analysis.subject.streamId,
256
+ );
257
+ if (configured?.presentation.enabled !== true) {
258
+ throw new CliError(
259
+ "not-found",
260
+ `Placement ${placement.placementId} does not enable analyzed stream ${options.analysis.subject.streamId}.`,
261
+ );
262
+ }
263
+ const assetStartUs = projectToAssetUs(placement.sync, options.projectRange.startUs);
264
+ const assetEndUs = projectToAssetUs(placement.sync, options.projectRange.endUs);
265
+ if (
266
+ assetStartUs === null
267
+ || assetEndUs === null
268
+ || assetEndUs <= assetStartUs
269
+ || assetStartUs < options.analysis.coverage.range.startUs
270
+ || assetEndUs > options.analysis.coverage.range.endUs
271
+ ) {
272
+ throw new CliError(
273
+ "conflict",
274
+ "Requested camera range is not fully covered by the placement sync map and face analysis.",
275
+ );
276
+ }
277
+ const assetRange = { endUs: assetEndUs, startUs: assetStartUs };
278
+ const candidateTracks = selectedTrackIds(options.analysis, assetRange, options.selection);
279
+ const pixels = layerPixels(
280
+ configured.presentation,
281
+ options.outputWidth,
282
+ options.outputHeight,
283
+ );
284
+ let preparedFrames = mapFaceResultsToPreparedLayer(options.analysis.results, {
285
+ crop: configured.presentation.crop,
286
+ fit: configured.presentation.fit,
287
+ layerPixelHeight: pixels.height,
288
+ layerPixelWidth: pixels.width,
289
+ sourceDisplayAspect: faceCoordinateDisplayAspect(options.analysis.coordinateSpace),
290
+ });
291
+ let tracks = candidateTracks;
292
+ if (options.selection.kind === "largest") {
293
+ const dynamic = largestVisibleFaceFrames(preparedFrames, candidateTracks, assetRange);
294
+ preparedFrames = dynamic.frames;
295
+ tracks = dynamic.selectedTrackIds.length === 0
296
+ ? candidateTracks
297
+ : dynamic.selectedTrackIds;
298
+ }
299
+ const framing = planFaceFraming({
300
+ config: {
301
+ gapPolicy: gapPolicy(options.gapPolicy, options.analysis.config.tracking.maximumGapUs),
302
+ headroomRatio: options.headroom,
303
+ maximumZoom: options.maximumZoom,
304
+ paddingRatio: paddingRatio(options.framing),
305
+ requireAllSelectedFaces: options.requireAllSelectedFaces,
306
+ simplificationTolerance: 0.003,
307
+ smoothingTimeUs: Math.round(options.smoothingSeconds * 1_000_000),
308
+ },
309
+ frames: preparedFrames,
310
+ range: assetRange,
311
+ trackIds: tracks,
312
+ });
313
+ const keyframes = framing.map((keyframe, index) => {
314
+ const projectTimeUs = assetToProjectUs(placement.sync, keyframe.assetTimeUs);
315
+ if (projectTimeUs === null) {
316
+ throw new CliError("conflict", "A face keyframe falls outside the accepted placement sync map.");
317
+ }
318
+ const zoom = clamp(keyframe.zoom, options.minimumZoom, options.maximumZoom);
319
+ const halfViewport = 1 / (2 * zoom);
320
+ return {
321
+ outgoingEasing: index === framing.length - 1 ? { kind: "linear" as const } : options.easing,
322
+ pose: {
323
+ centerX: clamp(
324
+ keyframe.viewport.x + keyframe.viewport.width / 2,
325
+ halfViewport,
326
+ 1 - halfViewport,
327
+ ),
328
+ centerY: clamp(
329
+ keyframe.viewport.y + keyframe.viewport.height / 2,
330
+ halfViewport,
331
+ 1 - halfViewport,
332
+ ),
333
+ space: "prepared-video-layer-normalized-v1" as const,
334
+ zoom,
335
+ },
336
+ projectTimeUs: index === 0
337
+ ? options.projectRange.startUs
338
+ : index === framing.length - 1
339
+ ? options.projectRange.endUs
340
+ : projectTimeUs,
341
+ };
342
+ }).filter((keyframe, index, all) => (
343
+ index === 0 || keyframe.projectTimeUs > all[index - 1]!.projectTimeUs
344
+ ));
345
+ if (keyframes.length < 2 || keyframes.at(-1)?.projectTimeUs !== options.projectRange.endUs) {
346
+ throw new CliError("conflict", "Face evidence collapsed to an invalid camera keyframe timeline.");
347
+ }
348
+ const parsed = ProjectCameraMoveSchema.safeParse({
349
+ binding: {
350
+ geometrySha256: hashProjectCameraGeometry(
351
+ options.project,
352
+ placement.placementId,
353
+ options.analysis.subject.streamId,
354
+ ),
355
+ syncSha256: hashProjectCameraSync(placement),
356
+ },
357
+ cameraMoveId: options.cameraMoveId,
358
+ keyframes,
359
+ origin: {
360
+ analysisId: options.analysis.analysisId,
361
+ analysisSha256: options.reference.sha256,
362
+ assetId: options.analysis.subject.assetId,
363
+ assetRange,
364
+ kind: "face-analysis",
365
+ outputAspectRatio: configured.presentation.layout.kind === "normalized"
366
+ ? options.outputWidth / options.outputHeight
367
+ : null,
368
+ streamId: options.analysis.subject.streamId,
369
+ subjectIntegritySha256: options.analysis.subject.integritySha256,
370
+ trackIds: tracks,
371
+ },
372
+ placementId: placement.placementId,
373
+ projectRange: options.projectRange,
374
+ streamId: options.analysis.subject.streamId,
375
+ });
376
+ if (!parsed.success) {
377
+ throw new CliError(
378
+ "invalid-data",
379
+ `Face-follow camera move is invalid: ${parsed.error.issues[0]?.message ?? "unknown error"}`,
380
+ );
381
+ }
382
+ if (options.plan.cameraMoves.some(move => (
383
+ move.placementId === parsed.data.placementId
384
+ && move.streamId === parsed.data.streamId
385
+ && move.projectRange.startUs < parsed.data.projectRange.endUs
386
+ && move.projectRange.endUs > parsed.data.projectRange.startUs
387
+ ))) {
388
+ throw new CliError(
389
+ "conflict",
390
+ "Face-follow range overlaps an existing camera move on the same video layer.",
391
+ );
392
+ }
393
+ return { move: parsed.data, selectedTrackIds: tracks };
394
+ }