@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,284 @@
1
+ import {
2
+ ManualProjectCameraMoveInputV2Schema,
3
+ ManualProjectZoomInputV3Schema,
4
+ type ManualProjectCameraMoveInputV2,
5
+ type ManualProjectZoomInputV3,
6
+ type OrderedProjectEditV2,
7
+ type OrderedProjectEditV3,
8
+ } from "../application/operations/derive/edit-batch";
9
+ import {
10
+ CameraPoseSchema,
11
+ type CameraPose,
12
+ type Easing,
13
+ type ProjectEditPlanV1,
14
+ type ProjectPlacementId,
15
+ type SourceInterval,
16
+ type ZoomOperation,
17
+ type ZoomTarget,
18
+ } from "../contracts";
19
+ import type { OperationInputValue } from "./contracts";
20
+
21
+ type MetadataEffects = ProjectEditPlanV1["effects"];
22
+
23
+ /** Binding-free manual move accepted by a checked v2 project commit. */
24
+ export type ManualCameraMoveInput = ManualProjectCameraMoveInputV2;
25
+
26
+ /** Binding-free manual zoom accepted by a checked v3 project commit. */
27
+ export type ManualZoomInput = Omit<
28
+ ManualProjectZoomInputV3,
29
+ "displayId" | "placementId"
30
+ > & {
31
+ readonly displayId?: string;
32
+ readonly placementId?: NonNullable<
33
+ ManualProjectZoomInputV3["placementId"]
34
+ >;
35
+ };
36
+
37
+ export type SetMetadataEffectsEdit = Extract<
38
+ OrderedProjectEditV2,
39
+ { readonly kind: "set-metadata-effects" }
40
+ >;
41
+
42
+ export type AddManualCameraMovesEdit = Extract<
43
+ OrderedProjectEditV2,
44
+ { readonly kind: "add-manual-camera-moves" }
45
+ >;
46
+
47
+ export type RemoveCameraMovesEdit = Extract<
48
+ OrderedProjectEditV2,
49
+ { readonly kind: "remove-camera-moves" }
50
+ >;
51
+
52
+ export type RemoveZoomsEdit = Extract<
53
+ OrderedProjectEditV2,
54
+ { readonly kind: "remove-zooms" }
55
+ >;
56
+
57
+ export type AddManualZoomsEdit = Extract<
58
+ OrderedProjectEditV3,
59
+ { readonly kind: "add-manual-zooms" }
60
+ >;
61
+
62
+ export interface MetadataEffectsOptions {
63
+ readonly clicks: OperationInputValue<MetadataEffects["clicks"]>;
64
+ readonly cursor: OperationInputValue<MetadataEffects["cursor"]>;
65
+ readonly keystrokes: OperationInputValue<MetadataEffects["keystrokes"]>;
66
+ readonly metadataPlacementId: OperationInputValue<ProjectPlacementId | null>;
67
+ readonly typedText: OperationInputValue<MetadataEffects["typedText"]>;
68
+ }
69
+
70
+ /**
71
+ * Replace the complete interaction-effect configuration in one explicit edit.
72
+ * Requiring every field prevents an agent from depending on hidden prior state.
73
+ */
74
+ export function setMetadataEffects(
75
+ input: MetadataEffectsOptions,
76
+ ): OperationInputValue<SetMetadataEffectsEdit> {
77
+ return {
78
+ clicks: input.clicks,
79
+ cursor: input.cursor,
80
+ keystrokes: input.keystrokes,
81
+ kind: "set-metadata-effects",
82
+ metadataPlacementId: input.metadataPlacementId,
83
+ typedText: input.typedText,
84
+ };
85
+ }
86
+
87
+ /**
88
+ * Conservative screen-demo polish. Typed text stays off because capture is
89
+ * opt-in and secure-field suppression must never be weakened by a workflow.
90
+ */
91
+ export function polishedInteractionEffects(
92
+ metadataPlacementId: OperationInputValue<ProjectPlacementId>,
93
+ ): OperationInputValue<SetMetadataEffectsEdit> {
94
+ return setMetadataEffects({
95
+ clicks: {
96
+ color: "#5B8CFF",
97
+ durationUs: 450_000,
98
+ enabled: true,
99
+ radiusPx: 28,
100
+ style: "ring",
101
+ },
102
+ cursor: {
103
+ enabled: true,
104
+ scale: 1.15,
105
+ smoothing: {
106
+ algorithm: "exponential",
107
+ strength: 0.42,
108
+ },
109
+ style: "captured",
110
+ },
111
+ keystrokes: {
112
+ enabled: true,
113
+ holdUs: 900_000,
114
+ maxKeys: 6,
115
+ position: "bottom-center",
116
+ secureText: "hide",
117
+ },
118
+ metadataPlacementId,
119
+ typedText: { enabled: false },
120
+ });
121
+ }
122
+
123
+ export function addManualCameraMoves(
124
+ cameraMoves: readonly OperationInputValue<ManualProjectCameraMoveInputV2>[],
125
+ ): OperationInputValue<AddManualCameraMovesEdit> {
126
+ return {
127
+ cameraMoves,
128
+ kind: "add-manual-camera-moves",
129
+ };
130
+ }
131
+
132
+ export function addManualZooms(
133
+ zooms: readonly (
134
+ | ManualZoomInput
135
+ | OperationInputValue<ManualProjectZoomInputV3>
136
+ )[],
137
+ ): OperationInputValue<AddManualZoomsEdit> {
138
+ return {
139
+ kind: "add-manual-zooms",
140
+ zooms,
141
+ };
142
+ }
143
+
144
+ export function removeCameraMoves(
145
+ cameraMoveIds: readonly OperationInputValue<
146
+ ManualProjectCameraMoveInputV2["cameraMoveId"]
147
+ >[],
148
+ ): OperationInputValue<RemoveCameraMovesEdit> {
149
+ return {
150
+ cameraMoveIds,
151
+ kind: "remove-camera-moves",
152
+ };
153
+ }
154
+
155
+ export function removeZooms(
156
+ zoomIds: readonly OperationInputValue<ZoomOperation["zoomId"]>[],
157
+ ): OperationInputValue<RemoveZoomsEdit> {
158
+ return {
159
+ kind: "remove-zooms",
160
+ zoomIds,
161
+ };
162
+ }
163
+
164
+ export interface ManualZoomOptions {
165
+ readonly displayId?: string;
166
+ readonly easing?: Easing;
167
+ readonly enterDurationUs?: number;
168
+ readonly exitDurationUs?: number;
169
+ readonly placementId?: string;
170
+ readonly range: SourceInterval;
171
+ readonly scale: number;
172
+ readonly target: ZoomTarget;
173
+ readonly zoomId: string;
174
+ }
175
+
176
+ /**
177
+ * Construct binding-free manual zoom intent. The host selects omitted
178
+ * placement/display identities and binds the exact recording manifest and
179
+ * placement synchronization during v3 commit planning.
180
+ */
181
+ export function manualZoom(
182
+ input: ManualZoomOptions,
183
+ ): ManualZoomInput {
184
+ const durationUs = input.range.endUs - input.range.startUs;
185
+ const defaultTransitionUs = Math.min(
186
+ 300_000,
187
+ Math.max(0, Math.floor(durationUs / 2)),
188
+ );
189
+ const parsed = ManualProjectZoomInputV3Schema.parse({
190
+ ...input,
191
+ easing: input.easing ?? { kind: "ease-in-out" },
192
+ enterDurationUs: input.enterDurationUs ?? defaultTransitionUs,
193
+ exitDurationUs: input.exitDurationUs ?? defaultTransitionUs,
194
+ });
195
+ return {
196
+ easing: parsed.easing,
197
+ enterDurationUs: parsed.enterDurationUs,
198
+ exitDurationUs: parsed.exitDurationUs,
199
+ range: parsed.range,
200
+ scale: parsed.scale,
201
+ target: parsed.target,
202
+ zoomId: parsed.zoomId,
203
+ ...(parsed.displayId === undefined
204
+ ? {}
205
+ : { displayId: parsed.displayId }),
206
+ ...(parsed.placementId === undefined
207
+ ? {}
208
+ : { placementId: parsed.placementId }),
209
+ };
210
+ }
211
+
212
+ export interface PreparedCameraPoseInput {
213
+ readonly centerX: number;
214
+ readonly centerY: number;
215
+ readonly zoom: number;
216
+ }
217
+
218
+ export function preparedCameraPose(
219
+ input: PreparedCameraPoseInput,
220
+ ): CameraPose {
221
+ return CameraPoseSchema.parse({
222
+ ...input,
223
+ space: "prepared-video-layer-normalized-v1",
224
+ });
225
+ }
226
+
227
+ export interface CameraReframeOptions extends Omit<
228
+ ManualProjectCameraMoveInputV2,
229
+ "cameraMoveId" | "keyframes" | "placementId" | "streamId"
230
+ > {
231
+ readonly cameraMoveId: string;
232
+ readonly easing?: Easing;
233
+ readonly from: PreparedCameraPoseInput;
234
+ readonly placementId: string;
235
+ readonly streamId: string;
236
+ readonly to: PreparedCameraPoseInput;
237
+ }
238
+
239
+ /**
240
+ * Construct a binding-free manual camera move with exact endpoint keyframes.
241
+ * The checked commit binds it to the current placement synchronization and
242
+ * prepared-layer geometry.
243
+ */
244
+ export function cameraReframe(
245
+ input: CameraReframeOptions,
246
+ ): ManualProjectCameraMoveInputV2 {
247
+ const easing = input.easing ?? { kind: "ease-in-out" };
248
+ return ManualProjectCameraMoveInputV2Schema.parse({
249
+ cameraMoveId: input.cameraMoveId,
250
+ keyframes: [
251
+ {
252
+ outgoingEasing: easing,
253
+ pose: preparedCameraPose(input.from),
254
+ projectTimeUs: input.projectRange.startUs,
255
+ },
256
+ {
257
+ outgoingEasing: { kind: "linear" },
258
+ pose: preparedCameraPose(input.to),
259
+ projectTimeUs: input.projectRange.endUs,
260
+ },
261
+ ],
262
+ placementId: input.placementId,
263
+ projectRange: input.projectRange,
264
+ streamId: input.streamId,
265
+ });
266
+ }
267
+
268
+ export interface CameraPushOptions extends Omit<CameraReframeOptions, "from"> {
269
+ readonly from?: PreparedCameraPoseInput;
270
+ }
271
+
272
+ /**
273
+ * Construct the common two-keyframe push-in move. Use cameraReframe when the
274
+ * endpoint may zoom out or only pan.
275
+ */
276
+ export function cameraPush(
277
+ input: CameraPushOptions,
278
+ ): ManualProjectCameraMoveInputV2 {
279
+ const from = input.from ?? { centerX: 0.5, centerY: 0.5, zoom: 1 };
280
+ if (input.to.zoom <= from.zoom) {
281
+ throw new RangeError("A camera push must increase zoom from its first pose.");
282
+ }
283
+ return cameraReframe({ ...input, from });
284
+ }
@@ -0,0 +1,420 @@
1
+ import { ApplicationError } from "../application/errors";
2
+ import {
3
+ canonicalJson,
4
+ sha256Hex,
5
+ } from "../core/canonical-json";
6
+ import {
7
+ isOperationGraphNode,
8
+ type AuthoredWorkflowGraphV1,
9
+ type CandidateDescriptor,
10
+ type GraphInputValue,
11
+ type JsonValue,
12
+ } from "./contracts";
13
+ import type { NodeExecutionPlanningRequest } from "./scheduler";
14
+ import {
15
+ WorkflowFileCandidateSchema,
16
+ type WorkflowFileCandidate,
17
+ } from "./file-candidate";
18
+
19
+ const FILE_CANDIDATE_DOMAIN = "studio.workflow.file-candidate/v1";
20
+
21
+ interface FileClaim {
22
+ readonly bytes?: number;
23
+ readonly mediaType?: string;
24
+ readonly path: string;
25
+ readonly sha256?: string;
26
+ }
27
+
28
+ function record(value: unknown): value is Readonly<Record<string, unknown>> {
29
+ return typeof value === "object" && value !== null && !Array.isArray(value);
30
+ }
31
+
32
+ function reference(value: unknown): boolean {
33
+ return record(value) && Object.hasOwn(value, "$ref");
34
+ }
35
+
36
+ function field(value: unknown, key: string): unknown {
37
+ return record(value) ? value[key] : undefined;
38
+ }
39
+
40
+ function claim(value: unknown): FileClaim | undefined {
41
+ if (!record(value) || reference(value) || typeof value.path !== "string") {
42
+ return undefined;
43
+ }
44
+ return {
45
+ ...(typeof value.bytes === "number" ? { bytes: value.bytes } : {}),
46
+ ...(typeof value.mediaType === "string"
47
+ ? { mediaType: value.mediaType }
48
+ : {}),
49
+ path: value.path,
50
+ ...(typeof value.sha256 === "string" ? { sha256: value.sha256 } : {}),
51
+ };
52
+ }
53
+
54
+ function array(value: unknown): readonly unknown[] {
55
+ return Array.isArray(value) ? value : [];
56
+ }
57
+
58
+ function optionalClaim(value: unknown): readonly FileClaim[] {
59
+ const parsed = claim(value);
60
+ return parsed === undefined ? [] : [parsed];
61
+ }
62
+
63
+ function optionalPathClaim(value: unknown): readonly FileClaim[] {
64
+ if (typeof value === "string") return [{ path: value }];
65
+ return optionalClaim(value);
66
+ }
67
+
68
+ /**
69
+ * Returns only fields whose operation contract grants local-file authority.
70
+ * Other path-looking values (for example output destinations) are excluded.
71
+ */
72
+ export function operationFileClaims(
73
+ operation: string,
74
+ input: GraphInputValue | JsonValue,
75
+ ): readonly FileClaim[] {
76
+ if (!record(input)) return [];
77
+ if (operation === "slopcamera.studio.run") return optionalClaim(field(input, "bundle"));
78
+ if (operation === "scene.render") {
79
+ return [
80
+ ...optionalClaim(field(input, "source")),
81
+ ...array(field(input, "assets")).flatMap(asset => optionalClaim(field(asset, "artifact"))),
82
+ ];
83
+ }
84
+ if (operation === "media.ingest") {
85
+ return optionalClaim(Reflect.get(input, "source"));
86
+ }
87
+ if (operation === "media.overlay") {
88
+ const source = field(input, "source");
89
+ return record(source)
90
+ ? optionalClaim(field(source, "artifact"))
91
+ : [];
92
+ }
93
+ if (operation === "media.html-overlay") {
94
+ return [
95
+ ...optionalClaim(field(input, "document")),
96
+ ...array(field(input, "resources")).flatMap((resource) => (
97
+ record(resource)
98
+ ? optionalClaim(field(resource, "artifact"))
99
+ : []
100
+ )),
101
+ ];
102
+ }
103
+ if (
104
+ operation === "media.audio-effects"
105
+ || operation === "media.color-grade"
106
+ ) {
107
+ return optionalClaim(Reflect.get(input, "input"));
108
+ }
109
+ if (
110
+ operation === "slopcamera.diagram.check"
111
+ || operation === "slopcamera.diagram.render"
112
+ ) {
113
+ return optionalPathClaim(Reflect.get(input, "path"));
114
+ }
115
+ if (operation === "slopcamera.image.vectorize") {
116
+ return optionalPathClaim(Reflect.get(input, "inputPath"));
117
+ }
118
+ if (operation === "gateway.image") {
119
+ return [
120
+ ...array(Reflect.get(input, "images")).flatMap(
121
+ value => claim(value) ?? [],
122
+ ),
123
+ ...optionalClaim(Reflect.get(input, "mask")),
124
+ ];
125
+ }
126
+ if (operation === "gateway.video") {
127
+ const frames = array(Reflect.get(input, "frames")).flatMap((value) => {
128
+ if (!record(value)) return [];
129
+ return optionalClaim(value.source);
130
+ });
131
+ return [
132
+ ...frames,
133
+ ...optionalClaim(Reflect.get(input, "promptImage")),
134
+ ...array(Reflect.get(input, "references")).flatMap(
135
+ value => claim(value) ?? [],
136
+ ),
137
+ ];
138
+ }
139
+ if (operation === "gateway.transcription") {
140
+ return optionalClaim(Reflect.get(input, "audio"));
141
+ }
142
+ return [];
143
+ }
144
+
145
+ function descriptorBody(
146
+ input: Omit<CandidateDescriptor, "descriptorSha256">,
147
+ ): Omit<CandidateDescriptor, "descriptorSha256"> {
148
+ return {
149
+ ...(input.bytes === undefined ? {} : { bytes: input.bytes }),
150
+ id: input.id,
151
+ kind: input.kind,
152
+ ...(input.mediaType === undefined ? {} : { mediaType: input.mediaType }),
153
+ ...(input.sha256 === undefined ? {} : { sha256: input.sha256 }),
154
+ };
155
+ }
156
+
157
+ export function fileCandidateDescriptor(
158
+ input: Omit<CandidateDescriptor, "descriptorSha256">,
159
+ ): CandidateDescriptor {
160
+ const body = descriptorBody(input);
161
+ return {
162
+ ...body,
163
+ descriptorSha256: sha256Hex(
164
+ `${FILE_CANDIDATE_DOMAIN}\0${canonicalJson(body)}`,
165
+ ),
166
+ };
167
+ }
168
+
169
+ function assertDescriptorIntegrity(candidate: CandidateDescriptor): void {
170
+ const expected = fileCandidateDescriptor(descriptorBody(candidate));
171
+ if (candidate.descriptorSha256 !== expected.descriptorSha256) {
172
+ throw new ApplicationError(
173
+ "invalid-data",
174
+ `Static file candidate ${candidate.id} has an invalid descriptor digest.`,
175
+ );
176
+ }
177
+ }
178
+
179
+ export function collectLiteralFileCandidates(
180
+ graph: AuthoredWorkflowGraphV1,
181
+ ): readonly CandidateDescriptor[] {
182
+ const claims = new Map<string, FileClaim>();
183
+ for (const node of graph.nodes) {
184
+ if (!isOperationGraphNode(node)) continue;
185
+ const operation = node.executor.operation.kind;
186
+ for (const authored of operationFileClaims(operation, node.input)) {
187
+ const existing = claims.get(authored.path);
188
+ if (
189
+ existing !== undefined
190
+ && (
191
+ (
192
+ existing.bytes !== undefined
193
+ && authored.bytes !== undefined
194
+ && existing.bytes !== authored.bytes
195
+ )
196
+ || (
197
+ existing.sha256 !== undefined
198
+ && authored.sha256 !== undefined
199
+ && existing.sha256 !== authored.sha256
200
+ )
201
+ || (
202
+ existing.mediaType !== undefined
203
+ && authored.mediaType !== undefined
204
+ && existing.mediaType !== authored.mediaType
205
+ )
206
+ )
207
+ ) {
208
+ throw new ApplicationError(
209
+ "conflict",
210
+ `File candidate ${authored.path} was authored with conflicting descriptors.`,
211
+ );
212
+ }
213
+ claims.set(authored.path, {
214
+ ...(existing?.bytes ?? authored.bytes) === undefined
215
+ ? {}
216
+ : { bytes: existing?.bytes ?? authored.bytes },
217
+ ...(existing?.mediaType ?? authored.mediaType) === undefined
218
+ ? {}
219
+ : { mediaType: existing?.mediaType ?? authored.mediaType },
220
+ path: authored.path,
221
+ ...(existing?.sha256 ?? authored.sha256) === undefined
222
+ ? {}
223
+ : { sha256: existing?.sha256 ?? authored.sha256 },
224
+ });
225
+ }
226
+ }
227
+ return [...claims.values()]
228
+ .map(candidate => fileCandidateDescriptor({
229
+ ...(candidate.bytes === undefined ? {} : { bytes: candidate.bytes }),
230
+ id: candidate.path,
231
+ kind: "file",
232
+ ...(candidate.mediaType === undefined
233
+ ? {}
234
+ : { mediaType: candidate.mediaType }),
235
+ ...(candidate.sha256 === undefined ? {} : { sha256: candidate.sha256 }),
236
+ }))
237
+ .sort((left, right) => left.id.localeCompare(right.id));
238
+ }
239
+
240
+ function declaredCandidate(
241
+ declaration: WorkflowFileCandidate,
242
+ ): CandidateDescriptor {
243
+ return fileCandidateDescriptor({
244
+ ...(declaration.bytes === undefined ? {} : { bytes: declaration.bytes }),
245
+ id: declaration.path,
246
+ kind: "file",
247
+ ...(declaration.mediaType === undefined
248
+ ? {}
249
+ : { mediaType: declaration.mediaType }),
250
+ ...(declaration.sha256 === undefined
251
+ ? {}
252
+ : { sha256: declaration.sha256 }),
253
+ });
254
+ }
255
+
256
+ export function collectDeclaredFileCandidates(
257
+ value: unknown,
258
+ ): readonly CandidateDescriptor[] {
259
+ if (Array.isArray(value)) return value.flatMap(collectDeclaredFileCandidates);
260
+ if (!record(value)) return [];
261
+ if (
262
+ Reflect.get(value, "kind") === "file"
263
+ && Reflect.get(value, "version") === "slopcamera-workflow-file-candidate-v1"
264
+ ) {
265
+ return [declaredCandidate(WorkflowFileCandidateSchema.parse(value))];
266
+ }
267
+ return Object.values(value).flatMap(collectDeclaredFileCandidates);
268
+ }
269
+
270
+ export function mergeFileCandidateDescriptors(
271
+ groups: readonly (readonly CandidateDescriptor[])[],
272
+ ): readonly CandidateDescriptor[] {
273
+ const merged = new Map<string, FileClaim>();
274
+ for (const candidate of groups.flat()) {
275
+ if (candidate.kind !== "file") continue;
276
+ assertDescriptorIntegrity(candidate);
277
+ const existing = merged.get(candidate.id);
278
+ const next: FileClaim = {
279
+ ...(candidate.bytes === undefined ? {} : { bytes: candidate.bytes }),
280
+ ...(candidate.mediaType === undefined
281
+ ? {}
282
+ : { mediaType: candidate.mediaType }),
283
+ path: candidate.id,
284
+ ...(candidate.sha256 === undefined ? {} : { sha256: candidate.sha256 }),
285
+ };
286
+ if (
287
+ existing !== undefined
288
+ && (
289
+ (
290
+ existing.bytes !== undefined
291
+ && next.bytes !== undefined
292
+ && existing.bytes !== next.bytes
293
+ )
294
+ || (
295
+ existing.sha256 !== undefined
296
+ && next.sha256 !== undefined
297
+ && existing.sha256 !== next.sha256
298
+ )
299
+ || (
300
+ existing.mediaType !== undefined
301
+ && next.mediaType !== undefined
302
+ && existing.mediaType !== next.mediaType
303
+ )
304
+ )
305
+ ) {
306
+ throw new ApplicationError(
307
+ "conflict",
308
+ `File candidate ${candidate.id} was declared with conflicting descriptors.`,
309
+ );
310
+ }
311
+ merged.set(candidate.id, {
312
+ ...(existing?.bytes ?? next.bytes) === undefined
313
+ ? {}
314
+ : { bytes: existing?.bytes ?? next.bytes },
315
+ ...(existing?.mediaType ?? next.mediaType) === undefined
316
+ ? {}
317
+ : { mediaType: existing?.mediaType ?? next.mediaType },
318
+ path: candidate.id,
319
+ ...(existing?.sha256 ?? next.sha256) === undefined
320
+ ? {}
321
+ : { sha256: existing?.sha256 ?? next.sha256 },
322
+ });
323
+ }
324
+ return [...merged.values()]
325
+ .map(candidate => fileCandidateDescriptor({
326
+ ...(candidate.bytes === undefined ? {} : { bytes: candidate.bytes }),
327
+ id: candidate.path,
328
+ kind: "file",
329
+ ...(candidate.mediaType === undefined
330
+ ? {}
331
+ : { mediaType: candidate.mediaType }),
332
+ ...(candidate.sha256 === undefined ? {} : { sha256: candidate.sha256 }),
333
+ }))
334
+ .sort((left, right) => left.id.localeCompare(right.id));
335
+ }
336
+
337
+ function exactClaimMatches(left: FileClaim, right: FileClaim): boolean {
338
+ return left.path === right.path
339
+ && (left.bytes === undefined || left.bytes === right.bytes)
340
+ && (left.sha256 === undefined || left.sha256 === right.sha256)
341
+ && (left.mediaType === undefined || left.mediaType === right.mediaType);
342
+ }
343
+
344
+ function candidateMatches(
345
+ requested: FileClaim,
346
+ candidate: CandidateDescriptor,
347
+ ): boolean {
348
+ return requested.path === candidate.id
349
+ && (candidate.bytes === undefined || candidate.bytes === requested.bytes)
350
+ && (
351
+ candidate.sha256 === undefined
352
+ || candidate.sha256 === requested.sha256
353
+ )
354
+ && (
355
+ candidate.mediaType === undefined
356
+ || candidate.mediaType === requested.mediaType
357
+ );
358
+ }
359
+
360
+ function collectExactClaims(value: unknown): readonly FileClaim[] {
361
+ if (Array.isArray(value)) return value.flatMap(collectExactClaims);
362
+ if (!record(value)) return [];
363
+ const direct = claim(value);
364
+ const nested = Object.values(value).flatMap(collectExactClaims);
365
+ if (
366
+ direct === undefined
367
+ || direct.bytes === undefined
368
+ || direct.sha256 === undefined
369
+ ) {
370
+ return nested;
371
+ }
372
+ return [direct, ...nested];
373
+ }
374
+
375
+ function hostDependencyClaims(
376
+ request: NodeExecutionPlanningRequest,
377
+ ): readonly FileClaim[] {
378
+ const nodes = new Map(
379
+ request.graphPlan.graph.nodes.map(node => [node.key, node]),
380
+ );
381
+ return Object.entries(request.dependencyOutputs).flatMap(
382
+ ([nodeKey, output]) => {
383
+ const node = nodes.get(nodeKey);
384
+ return node !== undefined && isOperationGraphNode(node)
385
+ ? collectExactClaims(output.value)
386
+ : [];
387
+ },
388
+ );
389
+ }
390
+
391
+ export function assertOperationFileProvenance(
392
+ request: NodeExecutionPlanningRequest,
393
+ ): void {
394
+ const claims = operationFileClaims(
395
+ request.operation.kind,
396
+ request.resolvedInput,
397
+ );
398
+ if (claims.length === 0) return;
399
+ const candidates = request.graphPlan.staticBindings.candidates.filter(
400
+ candidate => candidate.kind === "file",
401
+ );
402
+ for (const candidate of candidates) assertDescriptorIntegrity(candidate);
403
+ const hostClaims = hostDependencyClaims(request);
404
+ for (const requested of claims) {
405
+ const authorizedByPlan = candidates.some(candidate =>
406
+ candidateMatches(requested, candidate));
407
+ const authorizedByHost = hostClaims.some(candidate =>
408
+ exactClaimMatches(requested, candidate));
409
+ if (!authorizedByPlan && !authorizedByHost) {
410
+ throw new ApplicationError(
411
+ "authorization-required",
412
+ `Node ${request.node.key} received undeclared local media: ${requested.path}`,
413
+ {
414
+ operation: request.operation.kind,
415
+ path: requested.path,
416
+ },
417
+ );
418
+ }
419
+ }
420
+ }