@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,209 @@
1
+ import { lstat, realpath } from "node:fs/promises"
2
+ import { tmpdir } from "node:os"
3
+ import { basename, dirname } from "node:path"
4
+ import {
5
+ MAX_VECTORIZE_REQUEST_BYTES,
6
+ MAX_VECTORIZE_RESPONSE_BYTES,
7
+ VECTORIZE_WORKER_PROTOCOL,
8
+ type VectorizeWorkerRequest,
9
+ type VectorizeWorkerResponse,
10
+ } from "./worker-protocol.js"
11
+ import {
12
+ VectorizeError,
13
+ type VectorizeErrorCode,
14
+ type VectorizeInput,
15
+ } from "./types.js"
16
+ import {
17
+ forwardVectorizeWorkerTermination,
18
+ withInheritedCommandFileDescriptors,
19
+ } from "./command.js"
20
+ import { vectorizeImageInProcess } from "./vectorize.js"
21
+ import { configureVectorizeSharpConcurrency } from "./pixels.js"
22
+
23
+ const errorCodes = new Set<VectorizeErrorCode>([
24
+ "input_limit",
25
+ "invalid_input",
26
+ "output_limit",
27
+ "quality_limit",
28
+ "timeout",
29
+ "tool_download",
30
+ "tool_integrity",
31
+ "tool_platform",
32
+ "tool_version",
33
+ "trace_failed",
34
+ "unsafe_svg",
35
+ ])
36
+
37
+ forwardVectorizeWorkerTermination()
38
+ await main()
39
+
40
+ async function main(): Promise<void> {
41
+ let response: VectorizeWorkerResponse
42
+ try {
43
+ configureVectorizeSharpConcurrency()
44
+ const requestText = await readBoundedInput()
45
+ const request = parseRequest(requestText)
46
+ const input = decodeInput(request.input)
47
+ const temporaryRoot = await validateTemporaryRoot(request.temporaryRoot)
48
+ const result = await withInheritedCommandFileDescriptors(
49
+ request.options.inheritedFileDescriptors,
50
+ async () => await vectorizeImageInProcess(
51
+ input,
52
+ request.options,
53
+ temporaryRoot,
54
+ ),
55
+ )
56
+ response = {
57
+ ok: true,
58
+ protocol: VECTORIZE_WORKER_PROTOCOL,
59
+ result,
60
+ }
61
+ } catch (error) {
62
+ response = {
63
+ error: serializeError(error),
64
+ ok: false,
65
+ protocol: VECTORIZE_WORKER_PROTOCOL,
66
+ }
67
+ }
68
+ let encoded = JSON.stringify(response)
69
+ if (Buffer.byteLength(encoded) > MAX_VECTORIZE_RESPONSE_BYTES) {
70
+ encoded = JSON.stringify({
71
+ error: {
72
+ code: "output_limit",
73
+ details: { maximumBytes: MAX_VECTORIZE_RESPONSE_BYTES },
74
+ message: "The vectorization worker response exceeds its IPC limit.",
75
+ },
76
+ ok: false,
77
+ protocol: VECTORIZE_WORKER_PROTOCOL,
78
+ } satisfies VectorizeWorkerResponse)
79
+ }
80
+ await Bun.write(Bun.stdout, encoded)
81
+ }
82
+
83
+ async function readBoundedInput(): Promise<string> {
84
+ const reader = Bun.stdin.stream().getReader()
85
+ const chunks: Uint8Array[] = []
86
+ let bytes = 0
87
+ try {
88
+ while (true) {
89
+ const { done, value } = await reader.read()
90
+ if (done) break
91
+ bytes += value.byteLength
92
+ if (bytes > MAX_VECTORIZE_REQUEST_BYTES) {
93
+ await reader.cancel()
94
+ throw new VectorizeError(
95
+ "input_limit",
96
+ "The vectorization worker request exceeds its IPC limit.",
97
+ { bytes, maximumBytes: MAX_VECTORIZE_REQUEST_BYTES },
98
+ )
99
+ }
100
+ chunks.push(value)
101
+ }
102
+ } finally {
103
+ reader.releaseLock()
104
+ }
105
+ return Buffer.concat(chunks, bytes).toString("utf8")
106
+ }
107
+
108
+ function parseRequest(text: string): VectorizeWorkerRequest {
109
+ let parsed: unknown
110
+ try {
111
+ parsed = JSON.parse(text)
112
+ } catch (error) {
113
+ throw new VectorizeError(
114
+ "invalid_input",
115
+ "The vectorization worker request is malformed.",
116
+ {},
117
+ { cause: error },
118
+ )
119
+ }
120
+ if (
121
+ !isRecord(parsed) ||
122
+ parsed.protocol !== VECTORIZE_WORKER_PROTOCOL ||
123
+ !isRecord(parsed.input) ||
124
+ !isRecord(parsed.options) ||
125
+ typeof parsed.temporaryRoot !== "string" ||
126
+ parsed.temporaryRoot.length === 0 ||
127
+ typeof parsed.input.kind !== "string" ||
128
+ typeof parsed.input.value !== "string" ||
129
+ !["bytes", "path"].includes(parsed.input.kind)
130
+ ) {
131
+ throw new VectorizeError(
132
+ "invalid_input",
133
+ "The vectorization worker request is invalid.",
134
+ )
135
+ }
136
+ return parsed as unknown as VectorizeWorkerRequest
137
+ }
138
+
139
+ function decodeInput(input: VectorizeWorkerRequest["input"]): VectorizeInput {
140
+ if (input.kind === "path") return input.value
141
+ if (
142
+ input.value.length > Math.ceil(MAX_VECTORIZE_REQUEST_BYTES / 3) * 4 ||
143
+ input.value.length % 4 !== 0
144
+ ) {
145
+ throw new VectorizeError("input_limit", "Encoded raster input exceeds its IPC limit.")
146
+ }
147
+ const bytes = Buffer.from(input.value, "base64")
148
+ if (bytes.toString("base64") !== input.value) {
149
+ throw new VectorizeError("invalid_input", "Encoded raster input is not canonical base64.")
150
+ }
151
+ return bytes
152
+ }
153
+
154
+ async function validateTemporaryRoot(path: string): Promise<string> {
155
+ let realRoot: string
156
+ let realTemporaryDirectory: string
157
+ try {
158
+ const resolved = await Promise.all([
159
+ realpath(path),
160
+ realpath(tmpdir()),
161
+ ])
162
+ realRoot = resolved[0]
163
+ realTemporaryDirectory = resolved[1]
164
+ const metadata = await lstat(path)
165
+ if (
166
+ !metadata.isDirectory() ||
167
+ metadata.isSymbolicLink() ||
168
+ dirname(realRoot) !== realTemporaryDirectory ||
169
+ !/^slopcamera-vectorize-[A-Za-z0-9_-]{6,}$/u.test(basename(realRoot))
170
+ ) {
171
+ throw new VectorizeError(
172
+ "invalid_input",
173
+ "The supervisor temporary directory is invalid.",
174
+ )
175
+ }
176
+ } catch (error) {
177
+ if (error instanceof VectorizeError) throw error
178
+ throw new VectorizeError(
179
+ "invalid_input",
180
+ "The supervisor temporary directory could not be verified.",
181
+ {},
182
+ { cause: error },
183
+ )
184
+ }
185
+ return realRoot
186
+ }
187
+
188
+ function serializeError(error: unknown): Readonly<{
189
+ code: VectorizeErrorCode
190
+ details: Readonly<Record<string, unknown>>
191
+ message: string
192
+ }> {
193
+ const code =
194
+ error instanceof VectorizeError && errorCodes.has(error.code)
195
+ ? error.code
196
+ : "trace_failed"
197
+ const message =
198
+ error instanceof Error ? error.message : "The vectorization worker failed."
199
+ const details =
200
+ error instanceof VectorizeError &&
201
+ Buffer.byteLength(JSON.stringify(error.details)) <= 64 * 1_024
202
+ ? error.details
203
+ : {}
204
+ return { code, details, message }
205
+ }
206
+
207
+ function isRecord(value: unknown): value is Record<string, unknown> {
208
+ return typeof value === "object" && value !== null && !Array.isArray(value)
209
+ }
package/src/version.ts ADDED
@@ -0,0 +1 @@
1
+ export const SLOPCAMERA_VERSION = "3.2.4" as const
@@ -0,0 +1,409 @@
1
+ import {
2
+ executeSlopcameraOperationWithLease,
3
+ isSlopcameraOperationCode,
4
+ parseSlopcameraOperationInput,
5
+ slopcameraOperationHostResourceClaims,
6
+ type SlopcameraOperationCode,
7
+ type SlopcameraOperationDependencies,
8
+ type SlopcameraOperationInputMap,
9
+ type SlopcameraOperationResultMap,
10
+ } from "./operations.js"
11
+ import {
12
+ createDefaultHostResourceCoordinator,
13
+ type HostResourceCoordinator,
14
+ type HostResourceLease,
15
+ } from "./host-resources.js"
16
+
17
+ const workflowIdPattern = /^[a-z0-9]+(?:[._-][a-z0-9]+)*$/u
18
+ const workflowStepIdPattern = /^[A-Za-z0-9]+(?:[._:-][A-Za-z0-9]+)*$/u
19
+ const defaultMaximumSteps = 64
20
+ const hardMaximumSteps = 256
21
+
22
+ export type SlopcameraWorkflowErrorCode =
23
+ | "INVALID_WORKFLOW"
24
+ | "INVALID_WORKFLOW_INPUT"
25
+ | "INVALID_WORKFLOW_STEP"
26
+ | "WORKFLOW_ABORTED"
27
+ | "WORKFLOW_FAILED"
28
+ | "WORKFLOW_STEP_FAILED"
29
+
30
+ export interface SlopcameraWorkflowStepReceipt {
31
+ readonly index: number
32
+ readonly id: string
33
+ readonly operation: SlopcameraOperationCode
34
+ }
35
+
36
+ export class SlopcameraWorkflowError extends Error {
37
+ readonly code: SlopcameraWorkflowErrorCode
38
+ readonly completedSteps: readonly SlopcameraWorkflowStepReceipt[]
39
+ readonly failedStep?: Readonly<{
40
+ id: string
41
+ operation: SlopcameraOperationCode
42
+ }>
43
+
44
+ constructor(
45
+ code: SlopcameraWorkflowErrorCode,
46
+ message: string,
47
+ options: {
48
+ readonly cause?: unknown
49
+ readonly completedSteps?: readonly SlopcameraWorkflowStepReceipt[]
50
+ readonly failedStep?: Readonly<{
51
+ id: string
52
+ operation: SlopcameraOperationCode
53
+ }>
54
+ } = {},
55
+ ) {
56
+ super(`[${code}] ${message}`, { cause: options.cause })
57
+ this.name = "SlopcameraWorkflowError"
58
+ this.code = code
59
+ this.completedSteps = Object.freeze(
60
+ [...(options.completedSteps ?? [])].sort(
61
+ (left, right) => left.index - right.index,
62
+ ),
63
+ )
64
+ if (options.failedStep !== undefined) {
65
+ this.failedStep = Object.freeze({ ...options.failedStep })
66
+ }
67
+ }
68
+ }
69
+
70
+ export interface SlopcameraWorkflowExecutorContext {
71
+ readonly hostResourceLease: HostResourceLease
72
+ readonly signal: AbortSignal
73
+ readonly stepId: string
74
+ }
75
+
76
+ export type SlopcameraWorkflowExecutor = <C extends SlopcameraOperationCode>(
77
+ code: C,
78
+ input: SlopcameraOperationInputMap[C],
79
+ context: SlopcameraWorkflowExecutorContext,
80
+ ) => Promise<SlopcameraOperationResultMap[C]>
81
+
82
+ export interface SlopcameraWorkflowContext {
83
+ readonly signal: AbortSignal
84
+ operation<C extends SlopcameraOperationCode>(
85
+ id: string,
86
+ code: C,
87
+ input: SlopcameraOperationInputMap[C],
88
+ ): Promise<SlopcameraOperationResultMap[C]>
89
+ }
90
+
91
+ export interface SlopcameraWorkflowDefinition<Input, Output> {
92
+ readonly id: string
93
+ readonly version: number
94
+ readonly parseInput: (value: unknown) => Input
95
+ readonly run: (
96
+ context: SlopcameraWorkflowContext,
97
+ input: Input,
98
+ ) => Output | Promise<Output>
99
+ }
100
+
101
+ export interface DefineSlopcameraWorkflowOptions<Input, Output>
102
+ extends SlopcameraWorkflowDefinition<Input, Output> {}
103
+
104
+ export interface RunSlopcameraWorkflowOptions {
105
+ /**
106
+ * Operation dependencies may also carry admission controls. Explicit
107
+ * workflow-level controls take precedence when both are present.
108
+ */
109
+ readonly dependencies?: SlopcameraOperationDependencies
110
+ readonly executor?: SlopcameraWorkflowExecutor
111
+ readonly hostResourceCoordinator?: HostResourceCoordinator
112
+ readonly maximumSteps?: number
113
+ readonly signal?: AbortSignal
114
+ readonly waitTimeoutMilliseconds?: number
115
+ }
116
+
117
+ export interface SlopcameraWorkflowRun<Output> {
118
+ readonly workflow: Readonly<{
119
+ id: string
120
+ version: number
121
+ }>
122
+ readonly output: Output
123
+ readonly steps: readonly SlopcameraWorkflowStepReceipt[]
124
+ }
125
+
126
+ function workflowError(
127
+ code: SlopcameraWorkflowErrorCode,
128
+ message: string,
129
+ ): never {
130
+ throw new SlopcameraWorkflowError(code, message)
131
+ }
132
+
133
+ function validateWorkflowId(id: unknown): asserts id is string {
134
+ if (
135
+ typeof id !== "string" ||
136
+ id.length < 1 ||
137
+ id.length > 80 ||
138
+ !workflowIdPattern.test(id)
139
+ ) {
140
+ workflowError(
141
+ "INVALID_WORKFLOW",
142
+ "Workflow id must be 1 through 80 lowercase letters, numbers, dots, underscores, or hyphens.",
143
+ )
144
+ }
145
+ }
146
+
147
+ function validateStepId(
148
+ id: unknown,
149
+ completedSteps: readonly SlopcameraWorkflowStepReceipt[] = [],
150
+ ): asserts id is string {
151
+ if (
152
+ typeof id !== "string" ||
153
+ id.length < 1 ||
154
+ id.length > 80 ||
155
+ !workflowStepIdPattern.test(id)
156
+ ) {
157
+ throw new SlopcameraWorkflowError(
158
+ "INVALID_WORKFLOW_STEP",
159
+ "Step id must be 1 through 80 letters, numbers, dots, underscores, colons, or hyphens.",
160
+ { completedSteps },
161
+ )
162
+ }
163
+ }
164
+
165
+ export function defineSlopcameraWorkflow<Input, Output>(
166
+ options: DefineSlopcameraWorkflowOptions<Input, Output>,
167
+ ): SlopcameraWorkflowDefinition<Input, Output> {
168
+ if (typeof options !== "object" || options === null) {
169
+ workflowError("INVALID_WORKFLOW", "Workflow definition must be an object.")
170
+ }
171
+ validateWorkflowId(options.id)
172
+ if (!Number.isSafeInteger(options.version) || options.version < 1) {
173
+ workflowError(
174
+ "INVALID_WORKFLOW",
175
+ "Workflow version must be a positive safe integer.",
176
+ )
177
+ }
178
+ if (typeof options.parseInput !== "function" || typeof options.run !== "function") {
179
+ workflowError(
180
+ "INVALID_WORKFLOW",
181
+ "Workflow definition requires parseInput and run functions.",
182
+ )
183
+ }
184
+ return Object.freeze({
185
+ id: options.id,
186
+ version: options.version,
187
+ parseInput: options.parseInput,
188
+ run: options.run,
189
+ })
190
+ }
191
+
192
+ function maximumSteps(value: number | undefined): number {
193
+ const resolved = value ?? defaultMaximumSteps
194
+ if (
195
+ !Number.isSafeInteger(resolved) ||
196
+ resolved < 1 ||
197
+ resolved > hardMaximumSteps
198
+ ) {
199
+ workflowError(
200
+ "INVALID_WORKFLOW",
201
+ `maximumSteps must be an integer from 1 through ${hardMaximumSteps}.`,
202
+ )
203
+ }
204
+ return resolved
205
+ }
206
+
207
+ function aborted(
208
+ completedSteps: readonly SlopcameraWorkflowStepReceipt[],
209
+ cause?: unknown,
210
+ ): SlopcameraWorkflowError {
211
+ return new SlopcameraWorkflowError(
212
+ "WORKFLOW_ABORTED",
213
+ "Workflow execution was aborted.",
214
+ { cause, completedSteps },
215
+ )
216
+ }
217
+
218
+ export async function runSlopcameraWorkflow<Input, Output>(
219
+ definition: SlopcameraWorkflowDefinition<Input, Output>,
220
+ value: unknown,
221
+ options: RunSlopcameraWorkflowOptions = {},
222
+ ): Promise<SlopcameraWorkflowRun<Awaited<Output>>> {
223
+ const normalized = defineSlopcameraWorkflow(definition)
224
+ const limit = maximumSteps(options.maximumSteps)
225
+ const signal = options.signal
226
+ ?? options.dependencies?.signal
227
+ ?? new AbortController().signal
228
+ const invoked = new Set<string>()
229
+ const completed: SlopcameraWorkflowStepReceipt[] = []
230
+ const dispatched: Promise<unknown>[] = []
231
+ let acceptingOperations = true
232
+ let nextIndex = 0
233
+
234
+ if (signal.aborted) throw aborted(completed)
235
+
236
+ let input: Input
237
+ try {
238
+ input = normalized.parseInput(value)
239
+ } catch (cause) {
240
+ throw new SlopcameraWorkflowError(
241
+ "INVALID_WORKFLOW_INPUT",
242
+ "Workflow input did not satisfy its parser.",
243
+ { cause },
244
+ )
245
+ }
246
+
247
+ const executor: SlopcameraWorkflowExecutor = options.executor
248
+ ?? (<C extends SlopcameraOperationCode>(
249
+ code: C,
250
+ operationInput: SlopcameraOperationInputMap[C],
251
+ context: SlopcameraWorkflowExecutorContext,
252
+ ) => executeSlopcameraOperationWithLease(
253
+ code,
254
+ operationInput,
255
+ context.hostResourceLease,
256
+ options.dependencies,
257
+ ))
258
+ const hostResourceCoordinator = options.hostResourceCoordinator
259
+ ?? options.dependencies?.hostResourceCoordinator
260
+ ?? createDefaultHostResourceCoordinator()
261
+ const waitTimeoutMilliseconds = options.waitTimeoutMilliseconds
262
+ ?? options.dependencies?.waitTimeoutMilliseconds
263
+
264
+ async function dispatchOperation<C extends SlopcameraOperationCode>(
265
+ id: string,
266
+ code: C,
267
+ operationInput: SlopcameraOperationInputMap[C],
268
+ ): Promise<SlopcameraOperationResultMap[C]> {
269
+ if (signal.aborted) throw aborted(completed)
270
+ validateStepId(id, completed)
271
+ if (!isSlopcameraOperationCode(code)) {
272
+ throw new SlopcameraWorkflowError(
273
+ "INVALID_WORKFLOW_STEP",
274
+ `Workflow step ${id} names an unknown Slopcamera operation.`,
275
+ { completedSteps: completed },
276
+ )
277
+ }
278
+ if (invoked.has(id)) {
279
+ throw new SlopcameraWorkflowError(
280
+ "INVALID_WORKFLOW_STEP",
281
+ `Duplicate workflow step id: ${id}.`,
282
+ { completedSteps: completed },
283
+ )
284
+ }
285
+ if (nextIndex >= limit) {
286
+ throw new SlopcameraWorkflowError(
287
+ "INVALID_WORKFLOW_STEP",
288
+ `Workflow exceeds its ${String(limit)}-step limit.`,
289
+ { completedSteps: completed },
290
+ )
291
+ }
292
+ const index = nextIndex
293
+ nextIndex += 1
294
+ invoked.add(id)
295
+ let normalizedInput: SlopcameraOperationInputMap[C]
296
+ try {
297
+ normalizedInput = parseSlopcameraOperationInput(code, operationInput)
298
+ } catch (cause) {
299
+ throw new SlopcameraWorkflowError(
300
+ "INVALID_WORKFLOW_STEP",
301
+ `Workflow step ${id} has invalid input for ${code}.`,
302
+ { cause, completedSteps: completed },
303
+ )
304
+ }
305
+ try {
306
+ const result = await hostResourceCoordinator.withLease(
307
+ slopcameraOperationHostResourceClaims(code),
308
+ async (hostResourceLease) => await executor(code, normalizedInput, {
309
+ hostResourceLease,
310
+ signal,
311
+ stepId: id,
312
+ }),
313
+ {
314
+ signal,
315
+ ...(waitTimeoutMilliseconds === undefined
316
+ ? {}
317
+ : { waitTimeoutMilliseconds }),
318
+ },
319
+ )
320
+ completed.push(Object.freeze({ id, index, operation: code }))
321
+ if (signal.aborted) throw aborted(completed)
322
+ return result
323
+ } catch (cause) {
324
+ if (
325
+ cause instanceof SlopcameraWorkflowError &&
326
+ cause.code === "WORKFLOW_ABORTED"
327
+ ) {
328
+ throw cause
329
+ }
330
+ if (signal.aborted) throw aborted(completed, cause)
331
+ throw new SlopcameraWorkflowError(
332
+ "WORKFLOW_STEP_FAILED",
333
+ `Workflow step ${id} (${code}) failed.`,
334
+ {
335
+ cause,
336
+ completedSteps: completed,
337
+ failedStep: { id, operation: code },
338
+ },
339
+ )
340
+ }
341
+ }
342
+
343
+ const context: SlopcameraWorkflowContext = Object.freeze({
344
+ signal,
345
+ operation<C extends SlopcameraOperationCode>(
346
+ id: string,
347
+ code: C,
348
+ operationInput: SlopcameraOperationInputMap[C],
349
+ ): Promise<SlopcameraOperationResultMap[C]> {
350
+ if (!acceptingOperations) {
351
+ const closed = Promise.reject<SlopcameraOperationResultMap[C]>(
352
+ new SlopcameraWorkflowError(
353
+ "INVALID_WORKFLOW_STEP",
354
+ "Workflow operations cannot start after authored workflow code has settled.",
355
+ { completedSteps: completed },
356
+ ),
357
+ )
358
+ void closed.catch(() => undefined)
359
+ return closed
360
+ }
361
+ const operation = dispatchOperation(id, code, operationInput)
362
+ dispatched.push(operation)
363
+ // Attach a rejection observer immediately so a deliberately un-awaited
364
+ // branch cannot become an unhandled rejection before the runner drains it.
365
+ void operation.catch(() => undefined)
366
+ return operation
367
+ },
368
+ })
369
+
370
+ let output!: Awaited<Output>
371
+ let runFailed = false
372
+ let runFailure: unknown
373
+ try {
374
+ output = await normalized.run(context, input) as Awaited<Output>
375
+ } catch (cause) {
376
+ runFailed = true
377
+ runFailure = cause
378
+ } finally {
379
+ acceptingOperations = false
380
+ }
381
+
382
+ const operationResults = await Promise.allSettled(dispatched)
383
+ if (signal.aborted) throw aborted(completed, runFailed ? runFailure : undefined)
384
+ if (runFailed) {
385
+ if (runFailure instanceof SlopcameraWorkflowError) throw runFailure
386
+ throw new SlopcameraWorkflowError(
387
+ "WORKFLOW_FAILED",
388
+ `Workflow ${normalized.id} failed in authored code.`,
389
+ { cause: runFailure, completedSteps: completed },
390
+ )
391
+ }
392
+ const operationFailure = operationResults.find(
393
+ (result): result is PromiseRejectedResult => result.status === "rejected",
394
+ )
395
+ if (operationFailure !== undefined) {
396
+ const cause: unknown = operationFailure.reason
397
+ if (cause instanceof SlopcameraWorkflowError) throw cause
398
+ throw new SlopcameraWorkflowError(
399
+ "WORKFLOW_STEP_FAILED",
400
+ "A dispatched workflow operation failed.",
401
+ { cause, completedSteps: completed },
402
+ )
403
+ }
404
+ return Object.freeze({
405
+ workflow: Object.freeze({ id: normalized.id, version: normalized.version }),
406
+ output,
407
+ steps: Object.freeze([...completed].sort((left, right) => left.index - right.index)),
408
+ })
409
+ }