@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,953 @@
1
+ import { z } from "zod"
2
+
3
+ import {
4
+ AuthoredWorkflowGraphV1Schema,
5
+ CompiledWorkflowGraphSchema,
6
+ GraphCompilerLimitsSchema,
7
+ MAX_OPERATION_DISCOVERY_ENTRIES,
8
+ MAX_SERIALIZED_GRAPH_NODES,
9
+ OPERATION_PREPARATION_KINDS,
10
+ OPERATION_RESOURCE_KINDS,
11
+ REQUIREMENT_ENVELOPE_VERSION,
12
+ RequirementEnvelopeSchema,
13
+ Sha256Schema,
14
+ UNRESOLVED_REQUIREMENT_KINDS,
15
+ WORKFLOW_COMPILATION_VERSION,
16
+ WORKFLOW_EFFECT_CLASSES,
17
+ WORKFLOW_GRAPH_VERSION,
18
+ WORKFLOW_REF_VERSION,
19
+ WORKFLOW_RESUME_CLASSES,
20
+ isComputeGraphNode,
21
+ isOperationGraphNode,
22
+ trustedComputePolicy,
23
+ type AuthoredGraphNodeV1,
24
+ type AuthoredWorkflowGraphV1,
25
+ type CompiledWorkflowGraph,
26
+ type GraphCompilerLimits,
27
+ type GraphInputValue,
28
+ type OperationDiscoverySource,
29
+ type OperationKind,
30
+ type OperationPreparationKind,
31
+ type OperationResourceKind,
32
+ type RequirementEnvelope,
33
+ type SerializedRefV1,
34
+ type WorkflowEffectClass,
35
+ type WorkflowNodePolicy,
36
+ type WorkflowOutputBinding,
37
+ type WorkflowRegistryProjection,
38
+ type WorkflowResumeClass,
39
+ } from "./contracts.js"
40
+ import {
41
+ canonicalJsonSha256,
42
+ canonicalJsonSha256Prefixed,
43
+ } from "./canonical-json.js"
44
+ import { parseCodeBoundary } from "./boundary.js"
45
+ import { SlopcameraCodeError } from "./errors.js"
46
+ import {
47
+ createBoundedJsonValueSnapshot,
48
+ deepFreezeJson,
49
+ } from "./json-snapshot.js"
50
+ import {
51
+ PUBLIC_WORKFLOW_REGISTRY_PROJECTION,
52
+ createWorkflowRegistryProjection,
53
+ parseWorkflowRegistryProjection,
54
+ } from "./projection.js"
55
+
56
+ export const WORKFLOW_GRAPH_HASH_DOMAIN = "slopcamera.workflow.graph/v1" as const
57
+ export const WORKFLOW_COMPILATION_HASH_DOMAIN =
58
+ "slopcamera.workflow.compilation/v1" as const
59
+
60
+ export const DEFAULT_GRAPH_COMPILER_LIMITS = Object.freeze({
61
+ maxDepth: 64,
62
+ maxEdges: 2_048,
63
+ maxFanOut: 64,
64
+ maxNodes: 256,
65
+ maxTotalOperationFanOut: 4_096,
66
+ }) satisfies GraphCompilerLimits
67
+
68
+ const MAX_SERIALIZED_GRAPH_BYTES = 64 * 1024 * 1024
69
+ const MAX_SERIALIZED_GRAPH_DEPTH = 160
70
+ const MAX_SERIALIZED_GRAPH_VALUES = 1_100_000
71
+ const MAX_GRAPH_COMPILER_LIMIT_BYTES = 1_024
72
+ const MAX_GRAPH_COMPILER_LIMIT_DEPTH = 2
73
+ const MAX_GRAPH_COMPILER_LIMIT_INPUT_VALUES = 16
74
+ // A compilation contains the graph, registry projection, topology, limits,
75
+ // and requirement envelope. Its bounds must compose rather than reuse the
76
+ // graph's exact ceiling.
77
+ const MAX_WORKFLOW_COMPILATION_BYTES = 80 * 1024 * 1024
78
+ const MAX_WORKFLOW_COMPILATION_DEPTH = 192
79
+ const MAX_OPERATION_DISCOVERY_VALUES = 17
80
+ + OPERATION_PREPARATION_KINDS.length
81
+ + (3 * OPERATION_RESOURCE_KINDS.length)
82
+ const MAX_WORKFLOW_PROJECTION_VALUES = 5
83
+ + (MAX_OPERATION_DISCOVERY_ENTRIES * MAX_OPERATION_DISCOVERY_VALUES)
84
+ const MAX_REQUIREMENT_ENVELOPE_VALUES = 21
85
+ + (3 * MAX_SERIALIZED_GRAPH_NODES)
86
+ + WORKFLOW_EFFECT_CLASSES.length
87
+ + OPERATION_PREPARATION_KINDS.length
88
+ + (3 * OPERATION_RESOURCE_KINDS.length)
89
+ + WORKFLOW_RESUME_CLASSES.length
90
+ + UNRESOLVED_REQUIREMENT_KINDS.length
91
+ const MAX_TOPOLOGICAL_WAVE_VALUES = 1 + (2 * MAX_SERIALIZED_GRAPH_NODES)
92
+ const MAX_GRAPH_COMPILER_LIMIT_VALUES = 6
93
+ const MAX_WORKFLOW_COMPILATION_VALUES = MAX_SERIALIZED_GRAPH_VALUES
94
+ + MAX_WORKFLOW_PROJECTION_VALUES
95
+ + MAX_REQUIREMENT_ENVELOPE_VALUES
96
+ + MAX_TOPOLOGICAL_WAVE_VALUES
97
+ + MAX_GRAPH_COMPILER_LIMIT_VALUES
98
+ + 4 // Compilation root plus its three scalar fields.
99
+
100
+ export interface CompileWorkflowGraphOptions {
101
+ readonly graph: unknown
102
+ readonly limits?: unknown
103
+ readonly projection?: unknown
104
+ readonly projectionId?: string
105
+ readonly registry?: OperationDiscoverySource
106
+ readonly trustedCompute?: boolean
107
+ }
108
+
109
+ export interface ValidatedGraphTopology {
110
+ readonly depth: number
111
+ readonly edges: number
112
+ readonly structuralFanOut: number
113
+ readonly waves: readonly (readonly string[])[]
114
+ }
115
+ interface ValidatedGraph {
116
+ readonly graph: AuthoredWorkflowGraphV1
117
+ readonly policiesByNode: ReadonlyMap<string, WorkflowNodePolicy>
118
+ readonly topology: ValidatedGraphTopology
119
+ }
120
+
121
+ function invalidData(
122
+ message: string,
123
+ details?: Readonly<Record<string, unknown>>,
124
+ ): never {
125
+ throw new SlopcameraCodeError("invalid-data", message, details)
126
+ }
127
+
128
+ function deepFreeze<Value>(value: Value): Value {
129
+ if (typeof value !== "object" || value === null || Object.isFrozen(value)) {
130
+ return value
131
+ }
132
+ for (const nested of Object.values(value)) deepFreeze(nested)
133
+ return Object.freeze(value)
134
+ }
135
+
136
+ function operationKey(kind: OperationKind, version: number): string {
137
+ return `${kind}@${String(version)}`
138
+ }
139
+
140
+ function uniqueSorted<Value extends string>(
141
+ values: Iterable<Value>,
142
+ ): readonly Value[] {
143
+ return [...new Set(values)].sort((left, right) => left.localeCompare(right))
144
+ }
145
+
146
+ function canonicalSlopcameraIdentity(value: string): string {
147
+ if (value === "studio") return "slopcamera"
148
+ return value.replace(/^studio\./u, "slopcamera.")
149
+ }
150
+
151
+ function safeAdd(left: number, right: number, name: string): number {
152
+ const result = left + right
153
+ if (!Number.isSafeInteger(result)) {
154
+ return invalidData(`${name} exceeds the safe integer range.`, { left, right })
155
+ }
156
+ return result
157
+ }
158
+
159
+ function normalizeLimits(input: unknown): GraphCompilerLimits {
160
+ if (input === undefined) return DEFAULT_GRAPH_COMPILER_LIMITS
161
+ const captured = createBoundedJsonValueSnapshot(
162
+ input,
163
+ MAX_GRAPH_COMPILER_LIMIT_BYTES,
164
+ "graph compiler limits",
165
+ {
166
+ maximumDepth: MAX_GRAPH_COMPILER_LIMIT_DEPTH,
167
+ maximumValues: MAX_GRAPH_COMPILER_LIMIT_INPUT_VALUES,
168
+ },
169
+ ).value
170
+ if (typeof captured !== "object" || captured === null || Array.isArray(captured)) {
171
+ return parseCodeBoundary(
172
+ GraphCompilerLimitsSchema,
173
+ captured,
174
+ "graph compiler limits",
175
+ )
176
+ }
177
+ return parseCodeBoundary(GraphCompilerLimitsSchema, {
178
+ ...DEFAULT_GRAPH_COMPILER_LIMITS,
179
+ ...captured,
180
+ }, "graph compiler limits")
181
+ }
182
+
183
+ function preflightAuthoredWorkflowGraph(
184
+ graphInput: unknown,
185
+ maximumNodes: number,
186
+ ): void {
187
+ if (
188
+ typeof graphInput !== "object"
189
+ || graphInput === null
190
+ || Array.isArray(graphInput)
191
+ ) {
192
+ return
193
+ }
194
+ const nodes = Object.getOwnPropertyDescriptor(
195
+ graphInput,
196
+ "nodes",
197
+ )?.value as unknown
198
+ if (Array.isArray(nodes) && nodes.length > maximumNodes) {
199
+ return invalidData(
200
+ `Workflow has ${String(nodes.length)} nodes; the limit is ${String(maximumNodes)}.`,
201
+ { actual: nodes.length, limit: maximumNodes },
202
+ )
203
+ }
204
+ }
205
+
206
+ function parseAuthoredWorkflowGraphPreservingIdentity(
207
+ graphInput: unknown,
208
+ maximumNodes = MAX_SERIALIZED_GRAPH_NODES,
209
+ ): AuthoredWorkflowGraphV1 {
210
+ preflightAuthoredWorkflowGraph(graphInput, maximumNodes)
211
+ const captured = createBoundedJsonValueSnapshot(
212
+ graphInput,
213
+ MAX_SERIALIZED_GRAPH_BYTES,
214
+ "Authored workflow graph",
215
+ {
216
+ maximumDepth: MAX_SERIALIZED_GRAPH_DEPTH,
217
+ maximumValues: MAX_SERIALIZED_GRAPH_VALUES,
218
+ },
219
+ ).value
220
+ const graph = parseCodeBoundary(
221
+ AuthoredWorkflowGraphV1Schema,
222
+ captured,
223
+ "authored workflow graph",
224
+ )
225
+ if (graph.nodes.length > maximumNodes) {
226
+ return invalidData(
227
+ `Workflow has ${String(graph.nodes.length)} nodes; the limit is ${String(maximumNodes)}.`,
228
+ { actual: graph.nodes.length, limit: maximumNodes },
229
+ )
230
+ }
231
+ const sorted = {
232
+ ...graph,
233
+ nodes: [...graph.nodes].sort((left, right) => (
234
+ left.key.localeCompare(right.key)
235
+ )),
236
+ }
237
+ return deepFreezeJson(sorted)
238
+ }
239
+
240
+ function canonicalizeGraphValue(
241
+ value: GraphInputValue | WorkflowOutputBinding,
242
+ ): GraphInputValue | WorkflowOutputBinding {
243
+ if (isSerializedRef(value)) {
244
+ return {
245
+ $ref: {
246
+ ...value.$ref,
247
+ schemaId: canonicalSlopcameraIdentity(value.$ref.schemaId),
248
+ },
249
+ version: WORKFLOW_REF_VERSION,
250
+ }
251
+ }
252
+ if (Array.isArray(value)) {
253
+ return value.map(canonicalizeGraphValue) as readonly GraphInputValue[]
254
+ }
255
+ if (typeof value === "object" && value !== null) {
256
+ return Object.fromEntries(Object.entries(value).map(([key, nested]) => (
257
+ [key, canonicalizeGraphValue(nested)]
258
+ ))) as GraphInputValue
259
+ }
260
+ return value
261
+ }
262
+
263
+ function canonicalizeAuthoredWorkflowGraph(
264
+ graph: AuthoredWorkflowGraphV1,
265
+ ): AuthoredWorkflowGraphV1 {
266
+ return deepFreezeJson(parseCodeBoundary(AuthoredWorkflowGraphV1Schema, {
267
+ ...graph,
268
+ nodes: graph.nodes.map(node => ({
269
+ ...node,
270
+ executor: node.executor.kind === "operation"
271
+ ? {
272
+ kind: "operation" as const,
273
+ operation: {
274
+ ...node.executor.operation,
275
+ kind: canonicalSlopcameraIdentity(node.executor.operation.kind),
276
+ },
277
+ }
278
+ : {
279
+ compute: {
280
+ ...node.executor.compute,
281
+ key: canonicalSlopcameraIdentity(node.executor.compute.key),
282
+ },
283
+ kind: "compute" as const,
284
+ },
285
+ input: canonicalizeGraphValue(node.input),
286
+ inputSchemaId: canonicalSlopcameraIdentity(node.inputSchemaId),
287
+ outputSchemaId: canonicalSlopcameraIdentity(node.outputSchemaId),
288
+ })),
289
+ outputs: canonicalizeGraphValue(graph.outputs),
290
+ version: WORKFLOW_GRAPH_VERSION,
291
+ workflow: {
292
+ ...graph.workflow,
293
+ id: canonicalSlopcameraIdentity(graph.workflow.id),
294
+ inputSchemaId: canonicalSlopcameraIdentity(graph.workflow.inputSchemaId),
295
+ },
296
+ }, "canonical authored workflow graph"))
297
+ }
298
+
299
+ export function normalizeAuthoredWorkflowGraph(
300
+ graphInput: unknown,
301
+ maximumNodes = MAX_SERIALIZED_GRAPH_NODES,
302
+ ): AuthoredWorkflowGraphV1 {
303
+ return canonicalizeAuthoredWorkflowGraph(
304
+ parseAuthoredWorkflowGraphPreservingIdentity(graphInput, maximumNodes),
305
+ )
306
+ }
307
+
308
+ function workflowGraphHashFromNormalized(
309
+ graph: AuthoredWorkflowGraphV1,
310
+ ): string {
311
+ return canonicalJsonSha256Prefixed(
312
+ `${WORKFLOW_GRAPH_HASH_DOMAIN}\0`,
313
+ graph,
314
+ )
315
+ }
316
+
317
+ export function createWorkflowGraphHash(graphInput: unknown): string {
318
+ const graph = normalizeAuthoredWorkflowGraph(graphInput)
319
+ return workflowGraphHashFromNormalized(graph)
320
+ }
321
+
322
+ export const createGraphHash = createWorkflowGraphHash
323
+
324
+ function isSerializedRef(value: unknown): value is SerializedRefV1 {
325
+ return typeof value === "object"
326
+ && value !== null
327
+ && !Array.isArray(value)
328
+ && Object.hasOwn(value, "$ref")
329
+ }
330
+
331
+ function isGraphValueArray(
332
+ value: GraphInputValue | WorkflowOutputBinding,
333
+ ): value is readonly (GraphInputValue | WorkflowOutputBinding)[] {
334
+ return Array.isArray(value)
335
+ }
336
+
337
+ function collectReferences(
338
+ value: GraphInputValue | WorkflowOutputBinding,
339
+ ): readonly SerializedRefV1[] {
340
+ const references: SerializedRefV1[] = []
341
+ const pending: (GraphInputValue | WorkflowOutputBinding)[] = [value]
342
+ while (pending.length > 0) {
343
+ const current = pending.pop()
344
+ if (current === undefined) continue
345
+ if (isSerializedRef(current)) {
346
+ references.push(current)
347
+ continue
348
+ }
349
+ if (isGraphValueArray(current)) {
350
+ for (const item of current) pending.push(item)
351
+ continue
352
+ }
353
+ if (typeof current === "object" && current !== null) {
354
+ for (const item of Object.values(current)) pending.push(item)
355
+ }
356
+ }
357
+ return references
358
+ }
359
+
360
+ function arraysEqual(left: readonly string[], right: readonly string[]): boolean {
361
+ return left.length === right.length
362
+ && left.every((value, index) => value === right[index])
363
+ }
364
+
365
+ function validateReference(
366
+ reference: SerializedRefV1,
367
+ nodesByKey: ReadonlyMap<string, AuthoredGraphNodeV1>,
368
+ owner: string,
369
+ ): void {
370
+ const producer = nodesByKey.get(reference.$ref.nodeKey)
371
+ if (producer === undefined) {
372
+ return invalidData(
373
+ `${owner} contains a dangling reference to ${reference.$ref.nodeKey}.`,
374
+ { nodeKey: reference.$ref.nodeKey, owner },
375
+ )
376
+ }
377
+ if (producer.outputSchemaId !== reference.$ref.schemaId) {
378
+ return invalidData(
379
+ `${owner} expects schema ${reference.$ref.schemaId} from ${producer.key}, `
380
+ + `but the producer declares ${producer.outputSchemaId}.`,
381
+ {
382
+ actualSchemaId: producer.outputSchemaId,
383
+ expectedSchemaId: reference.$ref.schemaId,
384
+ nodeKey: producer.key,
385
+ owner,
386
+ },
387
+ )
388
+ }
389
+ }
390
+
391
+ function topologicalWaves(
392
+ nodes: readonly AuthoredGraphNodeV1[],
393
+ limits: GraphCompilerLimits,
394
+ ): ValidatedGraphTopology {
395
+ const dependencies = new Map<string, ReadonlySet<string>>()
396
+ const dependents = new Map<string, Set<string>>()
397
+ let edgeCount = 0
398
+ for (const node of nodes) {
399
+ const nodeDependencies = new Set(node.dependencies)
400
+ dependencies.set(node.key, nodeDependencies)
401
+ edgeCount = safeAdd(edgeCount, nodeDependencies.size, "Workflow edge count")
402
+ for (const dependency of nodeDependencies) {
403
+ const entries = dependents.get(dependency) ?? new Set<string>()
404
+ entries.add(node.key)
405
+ dependents.set(dependency, entries)
406
+ }
407
+ }
408
+ if (edgeCount > limits.maxEdges) {
409
+ return invalidData(
410
+ `Workflow has ${String(edgeCount)} edges; the limit is ${String(limits.maxEdges)}.`,
411
+ { actual: edgeCount, limit: limits.maxEdges },
412
+ )
413
+ }
414
+ const structuralFanOut = Math.max(
415
+ 0,
416
+ ...[...dependents.values()].map(entries => entries.size),
417
+ )
418
+ if (structuralFanOut > limits.maxFanOut) {
419
+ return invalidData(
420
+ `Workflow fan-out is ${String(structuralFanOut)}; the limit is ${String(limits.maxFanOut)}.`,
421
+ { actual: structuralFanOut, limit: limits.maxFanOut },
422
+ )
423
+ }
424
+
425
+ let ready = nodes
426
+ .filter(node => (dependencies.get(node.key)?.size ?? 0) === 0)
427
+ .map(node => node.key)
428
+ .sort((left, right) => left.localeCompare(right))
429
+ const remaining = new Map<string, number>(
430
+ nodes.map(node => [node.key, dependencies.get(node.key)?.size ?? 0]),
431
+ )
432
+ const waves: string[][] = []
433
+ let visited = 0
434
+ while (ready.length > 0) {
435
+ const wave = ready
436
+ waves.push(wave)
437
+ visited += wave.length
438
+ const next = new Set<string>()
439
+ for (const nodeKey of wave) {
440
+ for (const dependent of dependents.get(nodeKey) ?? []) {
441
+ const nextCount = (remaining.get(dependent) ?? 0) - 1
442
+ remaining.set(dependent, nextCount)
443
+ if (nextCount === 0) next.add(dependent)
444
+ }
445
+ }
446
+ ready = [...next].sort((left, right) => left.localeCompare(right))
447
+ }
448
+ if (visited !== nodes.length) {
449
+ return invalidData("Workflow graph contains a dependency cycle.")
450
+ }
451
+ if (waves.length > limits.maxDepth) {
452
+ return invalidData(
453
+ `Workflow depth is ${String(waves.length)}; the limit is ${String(limits.maxDepth)}.`,
454
+ { actual: waves.length, limit: limits.maxDepth },
455
+ )
456
+ }
457
+ return {
458
+ depth: waves.length,
459
+ edges: edgeCount,
460
+ structuralFanOut,
461
+ waves,
462
+ }
463
+ }
464
+
465
+ function validateGraph(
466
+ graphInput: unknown,
467
+ projection: WorkflowRegistryProjection,
468
+ limits: GraphCompilerLimits,
469
+ ): ValidatedGraph {
470
+ const graph = normalizeAuthoredWorkflowGraph(graphInput, limits.maxNodes)
471
+ if (graph.nodes.length > limits.maxNodes) {
472
+ return invalidData(
473
+ `Workflow has ${String(graph.nodes.length)} nodes; the limit is ${String(limits.maxNodes)}.`,
474
+ { actual: graph.nodes.length, limit: limits.maxNodes },
475
+ )
476
+ }
477
+ const nodesByKey = new Map<string, AuthoredGraphNodeV1>()
478
+ for (const node of graph.nodes) {
479
+ if (nodesByKey.has(node.key)) {
480
+ return invalidData(`Duplicate workflow node key: ${node.key}`, {
481
+ nodeKey: node.key,
482
+ })
483
+ }
484
+ nodesByKey.set(node.key, node)
485
+ }
486
+ const discoveryByKey = new Map(
487
+ projection.discovery.map(operation => [
488
+ operationKey(operation.kind, operation.version),
489
+ operation,
490
+ ]),
491
+ )
492
+ const policiesByNode = new Map<string, WorkflowNodePolicy>()
493
+ let totalOperationFanOut = 0
494
+ for (const node of graph.nodes) {
495
+ let policy: WorkflowNodePolicy
496
+ if (isOperationGraphNode(node)) {
497
+ const identity = node.executor.operation
498
+ const operation = discoveryByKey.get(
499
+ operationKey(identity.kind, identity.version),
500
+ )
501
+ if (operation === undefined) {
502
+ throw new SlopcameraCodeError(
503
+ "unsupported-plan",
504
+ `Unsupported operation: ${operationKey(identity.kind, identity.version)}`,
505
+ {
506
+ kind: identity.kind,
507
+ nodeKey: node.key,
508
+ projectionId: projection.id,
509
+ version: identity.version,
510
+ },
511
+ )
512
+ }
513
+ if (
514
+ node.inputSchemaId !== operation.inputSchemaId
515
+ || node.outputSchemaId !== operation.outputSchemaId
516
+ ) {
517
+ return invalidData(
518
+ `Operation schema identity mismatch for ${operationKey(identity.kind, identity.version)} `
519
+ + `at node ${node.key}.`,
520
+ {
521
+ actualInputSchemaId: node.inputSchemaId,
522
+ actualOutputSchemaId: node.outputSchemaId,
523
+ expectedInputSchemaId: operation.inputSchemaId,
524
+ expectedOutputSchemaId: operation.outputSchemaId,
525
+ kind: identity.kind,
526
+ nodeKey: node.key,
527
+ version: identity.version,
528
+ },
529
+ )
530
+ }
531
+ policy = operation.policy
532
+ } else if (isComputeGraphNode(node)) {
533
+ if (!projection.trustedCompute) {
534
+ throw new SlopcameraCodeError(
535
+ "unsupported-plan",
536
+ `Trusted compute is unsupported at node ${node.key}.`,
537
+ {
538
+ executorKind: "compute",
539
+ nodeKey: node.key,
540
+ projectionId: projection.id,
541
+ },
542
+ )
543
+ }
544
+ policy = trustedComputePolicy(node.executor.compute)
545
+ } else {
546
+ throw new SlopcameraCodeError(
547
+ "internal",
548
+ `Unknown node executor for ${node.key}.`,
549
+ )
550
+ }
551
+ policiesByNode.set(node.key, policy)
552
+ totalOperationFanOut = safeAdd(
553
+ totalOperationFanOut,
554
+ policy.maxFanOut,
555
+ "Total operation fan-out",
556
+ )
557
+ const references = collectReferences(node.input)
558
+ for (const reference of references) {
559
+ validateReference(reference, nodesByKey, `Node ${node.key}`)
560
+ }
561
+ const controlDependencies = node.controlDependencies ?? []
562
+ const normalizedControlDependencies = uniqueSorted(controlDependencies)
563
+ if (!arraysEqual(controlDependencies, normalizedControlDependencies)) {
564
+ return invalidData(
565
+ `Node ${node.key} control dependencies must be unique and sorted.`,
566
+ { nodeKey: node.key },
567
+ )
568
+ }
569
+ for (const dependency of controlDependencies) {
570
+ if (!nodesByKey.has(dependency)) {
571
+ return invalidData(
572
+ `Node ${node.key} contains a dangling control dependency to ${dependency}.`,
573
+ { dependency, nodeKey: node.key },
574
+ )
575
+ }
576
+ if (dependency === node.key) {
577
+ return invalidData(`Node ${node.key} cannot depend on itself.`, {
578
+ nodeKey: node.key,
579
+ })
580
+ }
581
+ }
582
+ const inferredDependencies = uniqueSorted([
583
+ ...references.map(reference => reference.$ref.nodeKey),
584
+ ...controlDependencies,
585
+ ])
586
+ if (!arraysEqual(node.dependencies, inferredDependencies)) {
587
+ return invalidData(
588
+ `Node ${node.key} dependencies do not match its embedded references and explicit control dependencies. `
589
+ + `Expected [${inferredDependencies.join(", ")}].`,
590
+ { expectedDependencies: inferredDependencies, nodeKey: node.key },
591
+ )
592
+ }
593
+ }
594
+ if (totalOperationFanOut > limits.maxTotalOperationFanOut) {
595
+ return invalidData(
596
+ `Workflow operation fan-out bound is ${String(totalOperationFanOut)}; `
597
+ + `the limit is ${String(limits.maxTotalOperationFanOut)}.`,
598
+ { actual: totalOperationFanOut, limit: limits.maxTotalOperationFanOut },
599
+ )
600
+ }
601
+ for (const reference of collectReferences(graph.outputs)) {
602
+ validateReference(reference, nodesByKey, "Workflow outputs")
603
+ }
604
+ return {
605
+ graph,
606
+ policiesByNode,
607
+ topology: topologicalWaves(graph.nodes, limits),
608
+ }
609
+ }
610
+
611
+ function operationFamily(kind: OperationKind): string {
612
+ const separator = kind.indexOf(".")
613
+ if (separator < 1) {
614
+ throw new SlopcameraCodeError(
615
+ "internal",
616
+ `Operation ${kind} has no namespace family.`,
617
+ { kind },
618
+ )
619
+ }
620
+ return kind.slice(0, separator)
621
+ }
622
+
623
+ function addResource(
624
+ resources: Map<OperationResourceKind, number>,
625
+ resource: OperationResourceKind,
626
+ amount: number,
627
+ ): void {
628
+ resources.set(
629
+ resource,
630
+ safeAdd(resources.get(resource) ?? 0, amount, `Resource ${resource} claim`),
631
+ )
632
+ }
633
+
634
+ function deriveRequirementEnvelope(validated: ValidatedGraph): RequirementEnvelope {
635
+ const computeKeys = new Set<string>()
636
+ const effects = new Set<WorkflowEffectClass>()
637
+ const families = new Set<string>()
638
+ const operationKinds = new Set<OperationKind>()
639
+ const preparation = new Set<OperationPreparationKind>()
640
+ const resources = new Map<OperationResourceKind, number>()
641
+ const resumeClasses = new Set<WorkflowResumeClass>()
642
+ let localMediaUploadNodes = 0
643
+ let maxDurationMs = 0
644
+ let maxInputBytes = 0
645
+ let maxOutputBytes = 0
646
+ let totalOperationFanOut = 0
647
+ for (const node of validated.graph.nodes) {
648
+ const policy = validated.policiesByNode.get(node.key)
649
+ if (policy === undefined) {
650
+ throw new SlopcameraCodeError(
651
+ "internal",
652
+ `Node ${node.key} lost its execution policy.`,
653
+ )
654
+ }
655
+ effects.add(policy.effect)
656
+ resumeClasses.add(policy.resume)
657
+ if (isOperationGraphNode(node)) {
658
+ families.add(operationFamily(node.executor.operation.kind))
659
+ operationKinds.add(node.executor.operation.kind)
660
+ } else if (isComputeGraphNode(node)) {
661
+ families.add("compute")
662
+ computeKeys.add(node.executor.compute.key)
663
+ }
664
+ for (const requirement of policy.preparation) preparation.add(requirement)
665
+ for (const claim of policy.resources) {
666
+ addResource(resources, claim.resource, claim.amount)
667
+ }
668
+ if (policy.effect === "paid-cloud" && policy.preparation.includes("local-media")) {
669
+ localMediaUploadNodes = safeAdd(
670
+ localMediaUploadNodes,
671
+ 1,
672
+ "Local media upload node count",
673
+ )
674
+ }
675
+ maxDurationMs = safeAdd(maxDurationMs, policy.maxDurationMs, "Workflow duration bound")
676
+ maxInputBytes = safeAdd(maxInputBytes, policy.maxInputBytes, "Workflow input byte bound")
677
+ maxOutputBytes = safeAdd(maxOutputBytes, policy.maxOutputBytes, "Workflow output byte bound")
678
+ totalOperationFanOut = safeAdd(
679
+ totalOperationFanOut,
680
+ policy.maxFanOut,
681
+ "Workflow operation fan-out bound",
682
+ )
683
+ }
684
+
685
+ const unresolved = new Set<RequirementEnvelope["unresolved"][number]>()
686
+ if (validated.topology.edges > 0) unresolved.add("dependency-outputs")
687
+ if (
688
+ preparation.has("local-media")
689
+ || preparation.has("project-state")
690
+ || preparation.has("recording-metadata")
691
+ ) {
692
+ unresolved.add("exact-media-hashes")
693
+ }
694
+ if (
695
+ preparation.has("recording-metadata")
696
+ || preparation.has("typed-text")
697
+ || preparation.has("window-metadata")
698
+ ) {
699
+ unresolved.add("exact-sensitive-metadata-hashes")
700
+ }
701
+ if (effects.has("paid-cloud") || resources.has("paid-call")) {
702
+ unresolved.add("prices")
703
+ }
704
+ if (
705
+ effects.has("project-mutation")
706
+ || operationKinds.has("render.project")
707
+ || operationKinds.has("render.project-plan")
708
+ ) {
709
+ unresolved.add("project-generations")
710
+ }
711
+ if (families.has("render")) unresolved.add("render-plans")
712
+
713
+ return parseCodeBoundary(RequirementEnvelopeSchema, {
714
+ bounds: {
715
+ depth: validated.topology.depth,
716
+ edges: validated.topology.edges,
717
+ localMediaUploadNodes,
718
+ maxDurationMs,
719
+ maxInputBytes,
720
+ maxOutputBytes,
721
+ nodes: validated.graph.nodes.length,
722
+ paidCallClaims: resources.get("paid-call") ?? 0,
723
+ structuralFanOut: validated.topology.structuralFanOut,
724
+ totalOperationFanOut,
725
+ },
726
+ computeKeys: uniqueSorted(computeKeys),
727
+ effects: uniqueSorted(effects),
728
+ operationFamilies: uniqueSorted(families),
729
+ operationKinds: uniqueSorted(operationKinds),
730
+ preparation: uniqueSorted(preparation),
731
+ resources: [...resources]
732
+ .map(([resource, amount]) => ({ amount, resource }))
733
+ .sort((left, right) => (
734
+ left.resource.localeCompare(right.resource)
735
+ )),
736
+ resumeClasses: uniqueSorted(resumeClasses),
737
+ unresolved: uniqueSorted(unresolved),
738
+ version: REQUIREMENT_ENVELOPE_VERSION,
739
+ }, "workflow requirement envelope")
740
+ }
741
+
742
+ function canonicalizeRequirementEnvelope(
743
+ input: unknown,
744
+ ): RequirementEnvelope {
745
+ const envelope = parseCodeBoundary(
746
+ RequirementEnvelopeSchema,
747
+ input,
748
+ "workflow requirement envelope",
749
+ )
750
+ return parseCodeBoundary(RequirementEnvelopeSchema, {
751
+ ...envelope,
752
+ computeKeys: envelope.computeKeys.map(canonicalSlopcameraIdentity),
753
+ operationFamilies: envelope.operationFamilies.map(canonicalSlopcameraIdentity),
754
+ operationKinds: envelope.operationKinds.map(canonicalSlopcameraIdentity),
755
+ version: REQUIREMENT_ENVELOPE_VERSION,
756
+ }, "canonical workflow requirement envelope")
757
+ }
758
+
759
+ function resolveProjection(
760
+ options: CompileWorkflowGraphOptions,
761
+ ): WorkflowRegistryProjection {
762
+ if (options.projection !== undefined) {
763
+ if (
764
+ options.registry !== undefined
765
+ || options.projectionId !== undefined
766
+ || options.trustedCompute !== undefined
767
+ ) {
768
+ throw new SlopcameraCodeError(
769
+ "invalid-data",
770
+ "Compile with either a projection or a registry projection source, not both.",
771
+ )
772
+ }
773
+ return parseWorkflowRegistryProjection(options.projection)
774
+ }
775
+ if (options.registry !== undefined) {
776
+ if (options.projectionId === undefined) {
777
+ throw new SlopcameraCodeError(
778
+ "invalid-data",
779
+ "A registry projection source requires an explicit projection id.",
780
+ )
781
+ }
782
+ return createWorkflowRegistryProjection(
783
+ options.projectionId,
784
+ options.registry,
785
+ { trustedCompute: options.trustedCompute ?? false },
786
+ )
787
+ }
788
+ if (options.projectionId !== undefined || options.trustedCompute !== undefined) {
789
+ throw new SlopcameraCodeError(
790
+ "invalid-data",
791
+ "A projection id or trusted-compute authority requires a registry projection source.",
792
+ )
793
+ }
794
+ return PUBLIC_WORKFLOW_REGISTRY_PROJECTION
795
+ }
796
+
797
+ type UnsignedWorkflowCompilation = Omit<CompiledWorkflowGraph, "compilationSha256">
798
+
799
+ const RequiredCompilationComponentSchema = z.unknown().refine(
800
+ value => value !== undefined,
801
+ "Required",
802
+ )
803
+ const ShallowCompiledWorkflowGraphSchema = z.strictObject({
804
+ compilationSha256: Sha256Schema,
805
+ envelope: RequiredCompilationComponentSchema,
806
+ graph: RequiredCompilationComponentSchema,
807
+ graphSha256: Sha256Schema,
808
+ limits: RequiredCompilationComponentSchema,
809
+ projection: RequiredCompilationComponentSchema,
810
+ topologicalWaves: RequiredCompilationComponentSchema,
811
+ version: z.literal(WORKFLOW_COMPILATION_VERSION),
812
+ })
813
+
814
+ function boundedCompilationInput(input: unknown, name: string): unknown {
815
+ return createBoundedJsonValueSnapshot(
816
+ input,
817
+ MAX_WORKFLOW_COMPILATION_BYTES,
818
+ name,
819
+ {
820
+ maximumDepth: MAX_WORKFLOW_COMPILATION_DEPTH,
821
+ maximumValues: MAX_WORKFLOW_COMPILATION_VALUES,
822
+ },
823
+ ).value
824
+ }
825
+
826
+ function workflowCompilationHashFromValidated(compilation: unknown): string {
827
+ return canonicalJsonSha256Prefixed(
828
+ `${WORKFLOW_COMPILATION_HASH_DOMAIN}\0`,
829
+ compilation,
830
+ )
831
+ }
832
+
833
+ export function createWorkflowCompilationHash(
834
+ compilationInput: unknown,
835
+ ): string {
836
+ const bounded = boundedCompilationInput(
837
+ compilationInput,
838
+ "workflow compilation",
839
+ )
840
+ if (typeof bounded !== "object" || bounded === null || Array.isArray(bounded)) {
841
+ return invalidData("Workflow compilation must be a plain object.")
842
+ }
843
+ const {
844
+ compilationSha256: ignoredCompilationSha256,
845
+ ...unsigned
846
+ } = bounded as Record<string, unknown>
847
+ void ignoredCompilationSha256
848
+ const parsed = parseCodeBoundary(
849
+ CompiledWorkflowGraphSchema.omit({ compilationSha256: true }),
850
+ unsigned,
851
+ "unsigned workflow compilation",
852
+ )
853
+ return workflowCompilationHashFromValidated(parsed)
854
+ }
855
+
856
+ export function compileWorkflowGraph(
857
+ options: CompileWorkflowGraphOptions,
858
+ ): CompiledWorkflowGraph {
859
+ const limits = normalizeLimits(options.limits)
860
+ const projection = resolveProjection(options)
861
+ const validated = validateGraph(options.graph, projection, limits)
862
+ const graphSha256 = workflowGraphHashFromNormalized(validated.graph)
863
+ const unsigned: UnsignedWorkflowCompilation = {
864
+ envelope: deriveRequirementEnvelope(validated),
865
+ graph: validated.graph,
866
+ graphSha256,
867
+ limits,
868
+ projection,
869
+ topologicalWaves: validated.topology.waves,
870
+ version: WORKFLOW_COMPILATION_VERSION,
871
+ }
872
+ return deepFreeze(parseCodeBoundary(CompiledWorkflowGraphSchema, {
873
+ ...unsigned,
874
+ compilationSha256: workflowCompilationHashFromValidated(unsigned),
875
+ }, "compiled workflow graph"))
876
+ }
877
+
878
+ export function parseCompiledWorkflowGraph(input: unknown): CompiledWorkflowGraph {
879
+ const bounded = boundedCompilationInput(input, "compiled workflow graph")
880
+ const shallow = parseCodeBoundary(
881
+ ShallowCompiledWorkflowGraphSchema,
882
+ bounded,
883
+ "compiled workflow graph",
884
+ )
885
+ const {
886
+ compilationSha256: parsedCompilationSha256,
887
+ ...unsigned
888
+ } = shallow
889
+ const expected = workflowCompilationHashFromValidated(unsigned)
890
+ if (parsedCompilationSha256 !== expected) {
891
+ throw new SlopcameraCodeError(
892
+ "invalid-data",
893
+ "Workflow compilation hash does not match its contents.",
894
+ {
895
+ actualCompilationSha256: parsedCompilationSha256,
896
+ expectedCompilationSha256: expected,
897
+ },
898
+ )
899
+ }
900
+ const parsed = parseCodeBoundary(
901
+ CompiledWorkflowGraphSchema,
902
+ bounded,
903
+ "compiled workflow graph",
904
+ )
905
+ const exactGraph = parseAuthoredWorkflowGraphPreservingIdentity(parsed.graph)
906
+ if (canonicalJsonSha256(parsed.graph) !== canonicalJsonSha256(exactGraph)) {
907
+ throw new SlopcameraCodeError(
908
+ "invalid-data",
909
+ "Compiled workflow graph nodes are not normalized.",
910
+ )
911
+ }
912
+ const expectedGraphSha256 = workflowGraphHashFromNormalized(exactGraph)
913
+ if (parsed.graphSha256 !== expectedGraphSha256) {
914
+ throw new SlopcameraCodeError(
915
+ "invalid-data",
916
+ "Workflow graph hash does not match its authenticated contents.",
917
+ {
918
+ actualGraphSha256: parsed.graphSha256,
919
+ expectedGraphSha256,
920
+ },
921
+ )
922
+ }
923
+ const canonicalGraph = canonicalizeAuthoredWorkflowGraph(exactGraph)
924
+ const canonicalProjection = parseWorkflowRegistryProjection(parsed.projection)
925
+ const canonicalLimits = normalizeLimits(parsed.limits)
926
+ const recompiled = compileWorkflowGraph({
927
+ graph: canonicalGraph,
928
+ limits: canonicalLimits,
929
+ projection: canonicalProjection,
930
+ })
931
+ const canonicalUnsigned: UnsignedWorkflowCompilation = {
932
+ envelope: canonicalizeRequirementEnvelope(parsed.envelope),
933
+ graph: canonicalGraph,
934
+ graphSha256: workflowGraphHashFromNormalized(canonicalGraph),
935
+ limits: canonicalLimits,
936
+ projection: canonicalProjection,
937
+ topologicalWaves: parsed.topologicalWaves,
938
+ version: WORKFLOW_COMPILATION_VERSION,
939
+ }
940
+ // The predecessor bytes and their nested graph/projection hashes were
941
+ // verified before identity normalization. Recompilation must then match the
942
+ // complete canonicalized semantics rather than the obsolete outer digest.
943
+ if (
944
+ recompiled.compilationSha256
945
+ !== workflowCompilationHashFromValidated(canonicalUnsigned)
946
+ ) {
947
+ throw new SlopcameraCodeError(
948
+ "invalid-data",
949
+ "Workflow compilation topology, requirements, or projection do not match the graph.",
950
+ )
951
+ }
952
+ return recompiled
953
+ }