@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,1188 @@
1
+ import {
2
+ PORTABLE_SLOPCAMERA_OPERATION_CONTRACTS,
3
+ PORTABLE_SLOPCAMERA_OPERATION_KINDS,
4
+ PUBLIC_WORKFLOW_REGISTRY_PROJECTION,
5
+ type PortableSlopcameraOperationKind,
6
+ } from "@hraness/slopcamera/code/advanced";
7
+ import {
8
+ executeSlopcameraOperation,
9
+ executeSlopcameraOperationWithLease,
10
+ parseSlopcameraOperationInput,
11
+ type SlopcameraOperationDependencies,
12
+ } from "@hraness/slopcamera/operations";
13
+ import { createHash } from "node:crypto";
14
+ import { constants, type BigIntStats, type Stats } from "node:fs";
15
+ import {
16
+ type FileHandle,
17
+ lstat,
18
+ open,
19
+ realpath,
20
+ } from "node:fs/promises";
21
+ import {
22
+ basename,
23
+ dirname,
24
+ isAbsolute,
25
+ join,
26
+ normalize,
27
+ relative,
28
+ resolve,
29
+ sep,
30
+ } from "node:path";
31
+ import { isDeepStrictEqual } from "node:util";
32
+
33
+ import {
34
+ ensurePrivateDirectory,
35
+ ensurePhysicalPrivateDirectoryWithin,
36
+ } from "../../cli/paths";
37
+ import { withMutationLock } from "../../cli/mutation-lock";
38
+ import { sha256Hex } from "../../core/canonical-json";
39
+ import { createNodeBundleFileSystem } from "../../core/storage";
40
+ import type {
41
+ ApplicationContext,
42
+ ApplicationHostResourceLease,
43
+ } from "../context";
44
+ import { ApplicationError } from "../errors";
45
+ import type {
46
+ OperationDefinition,
47
+ OperationExecutionContext,
48
+ OperationLifecycle,
49
+ } from "../operation";
50
+ import { MAXIMUM_DIAGRAM_SOURCE_BYTES } from "./slopcamera-visuals";
51
+ import { bindRepositoryMedia } from "./media/shared";
52
+
53
+ type PortableOperationContract =
54
+ typeof PORTABLE_SLOPCAMERA_OPERATION_CONTRACTS[PortableSlopcameraOperationKind];
55
+
56
+ const portableOperationContracts = Object.freeze(
57
+ PORTABLE_SLOPCAMERA_OPERATION_KINDS.map(
58
+ kind => PORTABLE_SLOPCAMERA_OPERATION_CONTRACTS[kind],
59
+ ),
60
+ );
61
+
62
+ const PORTABLE_OPERATION_COUNT = 4;
63
+
64
+ type PortableExecutor = (
65
+ kind: PortableSlopcameraOperationKind,
66
+ input: unknown,
67
+ dependencies?: SlopcameraOperationDependencies,
68
+ ) => Promise<unknown>;
69
+
70
+ type PortableLeasedExecutor = (
71
+ kind: PortableSlopcameraOperationKind,
72
+ input: unknown,
73
+ lease: ApplicationHostResourceLease,
74
+ dependencies?: SlopcameraOperationDependencies,
75
+ ) => Promise<unknown>;
76
+
77
+ export interface SlopcameraPortableOperationDependencies {
78
+ readonly execute?: PortableExecutor;
79
+ readonly executeWithLease?: PortableLeasedExecutor;
80
+ readonly parseInput?: (
81
+ kind: PortableSlopcameraOperationKind,
82
+ input: unknown,
83
+ ) => unknown;
84
+ }
85
+
86
+ const NONLOCAL_PATH_PATTERN = /^[A-Za-z][A-Za-z0-9+.-]*:/u;
87
+
88
+ function unsafePath(name: string): never {
89
+ throw new ApplicationError(
90
+ "unsafe-path",
91
+ `${name} must be a bounded local path inside the repository or an explicit absolute caller path.`,
92
+ );
93
+ }
94
+
95
+ function resolvePortablePath(
96
+ repositoryRootValue: string,
97
+ value: unknown,
98
+ name: string,
99
+ ): string {
100
+ if (
101
+ typeof value !== "string"
102
+ || value.length < 1
103
+ || value.length > 4_096
104
+ || value.includes("\0")
105
+ || NONLOCAL_PATH_PATTERN.test(value)
106
+ || value.startsWith("\\\\")
107
+ || value.startsWith("//")
108
+ ) {
109
+ return unsafePath(name);
110
+ }
111
+ if (isAbsolute(value)) return normalize(value);
112
+
113
+ let repositoryRoot: string;
114
+ let absolutePath: string;
115
+ try {
116
+ repositoryRoot = resolve(repositoryRootValue);
117
+ absolutePath = resolve(repositoryRoot, value);
118
+ } catch {
119
+ return unsafePath(name);
120
+ }
121
+ const repositoryRelativePath = relative(repositoryRoot, absolutePath);
122
+ if (
123
+ repositoryRelativePath === ".."
124
+ || repositoryRelativePath.startsWith(`..${sep}`)
125
+ || isAbsolute(repositoryRelativePath)
126
+ ) {
127
+ return unsafePath(name);
128
+ }
129
+ return absolutePath;
130
+ }
131
+
132
+ function inputRecord(value: unknown): Readonly<Record<string, unknown>> {
133
+ if (typeof value !== "object" || value === null || Array.isArray(value)) {
134
+ throw new ApplicationError(
135
+ "invalid-data",
136
+ "Portable Slopcamera operation input must be an object.",
137
+ );
138
+ }
139
+ return value as Readonly<Record<string, unknown>>;
140
+ }
141
+
142
+ function resolvePortableOperationPaths(
143
+ repositoryRoot: string,
144
+ kind: PortableSlopcameraOperationKind,
145
+ value: unknown,
146
+ ): Readonly<Record<string, unknown>> {
147
+ const input = inputRecord(value);
148
+ switch (kind) {
149
+ case "slopcamera.diagram.check":
150
+ return {
151
+ ...input,
152
+ path: resolvePortablePath(repositoryRoot, input.path, "path"),
153
+ };
154
+ case "slopcamera.diagram.render":
155
+ return {
156
+ ...input,
157
+ path: resolvePortablePath(repositoryRoot, input.path, "path"),
158
+ ...(input.outDirectory === undefined
159
+ ? {}
160
+ : {
161
+ outDirectory: resolvePortablePath(
162
+ repositoryRoot,
163
+ input.outDirectory,
164
+ "outDirectory",
165
+ ),
166
+ }),
167
+ };
168
+ case "slopcamera.image.vectorize":
169
+ return {
170
+ ...input,
171
+ inputPath: resolvePortablePath(
172
+ repositoryRoot,
173
+ input.inputPath,
174
+ "inputPath",
175
+ ),
176
+ outputPath: resolvePortablePath(
177
+ repositoryRoot,
178
+ input.outputPath,
179
+ "outputPath",
180
+ ),
181
+ };
182
+ case "slopcamera.image.generate":
183
+ return {
184
+ ...input,
185
+ outputPath: resolvePortablePath(
186
+ repositoryRoot,
187
+ input.outputPath,
188
+ "outputPath",
189
+ ),
190
+ };
191
+ }
192
+ }
193
+
194
+ const PORTABLE_INPUT_SNAPSHOT_ROOT = "portable-operation-inputs/v1";
195
+ const PORTABLE_OUTPUT_LEASE_ROOT = "portable-output-publication-leases";
196
+ const MAXIMUM_VECTOR_SOURCE_BYTES =
197
+ PORTABLE_SLOPCAMERA_OPERATION_CONTRACTS["slopcamera.image.vectorize"]
198
+ .policy.maxInputBytes;
199
+ const portableOutputLeaseTails = new Map<string, Promise<void>>();
200
+
201
+ interface PortableSnapshotIdentity {
202
+ readonly bytes: number;
203
+ readonly device: number;
204
+ readonly inode: number;
205
+ readonly modifiedAtMs: number;
206
+ readonly path: string;
207
+ readonly sha256: string;
208
+ }
209
+
210
+ interface PinnedPortableSnapshot {
211
+ readonly assertUnchanged: () => Promise<void>;
212
+ readonly close: () => Promise<void>;
213
+ readonly descriptor: number;
214
+ readonly input: Readonly<Record<string, unknown>>;
215
+ }
216
+
217
+ function repositoryRelativePath(
218
+ repositoryRootValue: string,
219
+ absolutePathValue: string,
220
+ name: string,
221
+ ): string {
222
+ const repositoryRoot = resolve(repositoryRootValue);
223
+ const absolutePath = resolve(absolutePathValue);
224
+ const repositoryPath = relative(repositoryRoot, absolutePath);
225
+ if (
226
+ repositoryPath === ""
227
+ || repositoryPath === ".."
228
+ || repositoryPath.startsWith(`..${sep}`)
229
+ || isAbsolute(repositoryPath)
230
+ ) {
231
+ throw new ApplicationError(
232
+ "unsafe-path",
233
+ `${name} must resolve to a physical file inside the repository for durable workflow execution.`,
234
+ );
235
+ }
236
+ return repositoryPath;
237
+ }
238
+
239
+ function pathWithin(root: string, candidate: string): boolean {
240
+ const fromRoot = relative(root, candidate);
241
+ return fromRoot === ""
242
+ || (
243
+ fromRoot !== ".."
244
+ && !fromRoot.startsWith(`..${sep}`)
245
+ && !isAbsolute(fromRoot)
246
+ );
247
+ }
248
+
249
+ function commonPhysicalBundleRoot(
250
+ sourcePath: string,
251
+ privateRoot: string,
252
+ ): string {
253
+ let root = dirname(resolve(sourcePath));
254
+ const destination = resolve(privateRoot);
255
+ while (!pathWithin(root, destination)) {
256
+ const parent = dirname(root);
257
+ if (parent === root) {
258
+ throw new ApplicationError(
259
+ "unsafe-path",
260
+ "Portable operation source and private storage do not share a local filesystem root.",
261
+ );
262
+ }
263
+ root = parent;
264
+ }
265
+ return root;
266
+ }
267
+
268
+ function noEntry(error: unknown): boolean {
269
+ return error instanceof Error
270
+ && "code" in error
271
+ && error.code === "ENOENT";
272
+ }
273
+
274
+ function portableOutputTarget(
275
+ kind: PortableSlopcameraOperationKind,
276
+ input: Readonly<Record<string, unknown>>,
277
+ ): string | undefined {
278
+ if (kind === "slopcamera.diagram.render") {
279
+ if (typeof input.outDirectory === "string") return input.outDirectory;
280
+ return typeof input.path === "string" ? dirname(input.path) : undefined;
281
+ }
282
+ if (
283
+ kind === "slopcamera.image.generate"
284
+ || kind === "slopcamera.image.vectorize"
285
+ ) {
286
+ return typeof input.outputPath === "string" ? input.outputPath : undefined;
287
+ }
288
+ return undefined;
289
+ }
290
+
291
+ async function canonicalPhysicalOutputTarget(
292
+ targetValue: string,
293
+ directoryTarget: boolean,
294
+ ): Promise<string> {
295
+ const target = resolve(targetValue);
296
+ const missing: string[] = [];
297
+ let existing = target;
298
+ let details;
299
+ while (true) {
300
+ try {
301
+ details = await lstat(existing);
302
+ break;
303
+ } catch (error) {
304
+ if (!noEntry(error)) throw error;
305
+ const parent = dirname(existing);
306
+ if (parent === existing) throw error;
307
+ missing.unshift(basename(existing));
308
+ existing = parent;
309
+ }
310
+ }
311
+ if (details.isSymbolicLink() && missing.length === 0 && !directoryTarget) {
312
+ throw new ApplicationError(
313
+ "unsafe-path",
314
+ "Portable operation file outputs must not replace an existing symlink.",
315
+ );
316
+ }
317
+ const physicalExisting = await realpath(existing);
318
+ const physicalDetails = await lstat(physicalExisting);
319
+ if (
320
+ physicalDetails.isSymbolicLink()
321
+ || (
322
+ missing.length > 0 || directoryTarget
323
+ ? !physicalDetails.isDirectory()
324
+ : !physicalDetails.isFile()
325
+ )
326
+ || (
327
+ !details.isSymbolicLink()
328
+ && (
329
+ physicalDetails.dev !== details.dev
330
+ || physicalDetails.ino !== details.ino
331
+ )
332
+ )
333
+ ) {
334
+ throw new ApplicationError(
335
+ "unsafe-path",
336
+ "Portable operation output target ancestry changed during canonicalization.",
337
+ );
338
+ }
339
+ return resolve(physicalExisting, ...missing);
340
+ }
341
+
342
+ async function canonicalizePortableOutputTarget(
343
+ kind: PortableSlopcameraOperationKind,
344
+ input: Readonly<Record<string, unknown>>,
345
+ repositoryRoot?: string,
346
+ ): Promise<Readonly<Record<string, unknown>>> {
347
+ const target = portableOutputTarget(kind, input);
348
+ if (target === undefined) return input;
349
+ const canonical = await canonicalPhysicalOutputTarget(
350
+ target,
351
+ kind === "slopcamera.diagram.render",
352
+ );
353
+ if (
354
+ repositoryRoot !== undefined
355
+ && !pathWithin(await realpath(repositoryRoot), canonical)
356
+ ) {
357
+ throw new ApplicationError(
358
+ "unsafe-path",
359
+ "Repository-relative portable output resolved outside the physical repository root.",
360
+ );
361
+ }
362
+ return kind === "slopcamera.diagram.render"
363
+ ? { ...input, outDirectory: canonical }
364
+ : { ...input, outputPath: canonical };
365
+ }
366
+
367
+ function portableOutputIsRepositoryRelative(
368
+ kind: PortableSlopcameraOperationKind,
369
+ input: Readonly<Record<string, unknown>>,
370
+ ): boolean {
371
+ const target = portableOutputTarget(kind, input);
372
+ return target !== undefined && !isAbsolute(target);
373
+ }
374
+
375
+ export function slopcameraPortableOutputPublicationParent(
376
+ kind: PortableSlopcameraOperationKind,
377
+ inputValue: unknown,
378
+ ): string | undefined {
379
+ const input = inputRecord(inputValue);
380
+ const target = portableOutputTarget(kind, input);
381
+ if (target === undefined) return undefined;
382
+ const parent = kind === "slopcamera.diagram.render"
383
+ ? target
384
+ : dirname(target);
385
+ return normalize(parent).normalize("NFC").toLowerCase();
386
+ }
387
+
388
+ async function portableOutputLeaseDirectory(
389
+ application: ApplicationContext,
390
+ publicationParent: string,
391
+ ): Promise<string> {
392
+ let physicalMutationRoot: string;
393
+ if (application.machineStateRoot !== undefined) {
394
+ await ensurePrivateDirectory(application.machineStateRoot);
395
+ physicalMutationRoot = await realpath(application.machineStateRoot);
396
+ } else {
397
+ const repositoryRoot = resolve(application.paths.repositoryRoot);
398
+ const privateRoot = repositoryRelativePath(
399
+ repositoryRoot,
400
+ application.paths.privateRoot,
401
+ "Application private root",
402
+ );
403
+ physicalMutationRoot = await ensurePhysicalPrivateDirectoryWithin(
404
+ repositoryRoot,
405
+ privateRoot,
406
+ );
407
+ }
408
+ const leaseRoot = await ensurePhysicalPrivateDirectoryWithin(
409
+ physicalMutationRoot,
410
+ PORTABLE_OUTPUT_LEASE_ROOT,
411
+ );
412
+ const key = sha256Hex(
413
+ `slopcamera.portable-output-publication-lease/v1\0${publicationParent}`,
414
+ );
415
+ return await ensurePhysicalPrivateDirectoryWithin(leaseRoot, key);
416
+ }
417
+
418
+ async function withPortableOutputQueue<Value>(
419
+ key: string,
420
+ execute: () => Promise<Value>,
421
+ ): Promise<Value> {
422
+ const previous = portableOutputLeaseTails.get(key) ?? Promise.resolve();
423
+ const result = previous.then(execute);
424
+ const settled = result.then(
425
+ () => undefined,
426
+ () => undefined,
427
+ );
428
+ portableOutputLeaseTails.set(key, settled);
429
+ try {
430
+ return await result;
431
+ } finally {
432
+ if (portableOutputLeaseTails.get(key) === settled) {
433
+ portableOutputLeaseTails.delete(key);
434
+ }
435
+ }
436
+ }
437
+
438
+ async function withPortableOutputPublicationLease<Value>(
439
+ application: ApplicationContext,
440
+ publicationParent: string,
441
+ execute: () => Promise<Value>,
442
+ ): Promise<Value> {
443
+ const directory = await portableOutputLeaseDirectory(
444
+ application,
445
+ publicationParent,
446
+ );
447
+ return await withPortableOutputQueue(directory, async () =>
448
+ await withMutationLock(directory, {
449
+ command: "workflow:slopcamera-portable",
450
+ label: publicationParent.slice(0, 512),
451
+ now: application.clock.now,
452
+ }, execute));
453
+ }
454
+
455
+ async function snapshotPortableSource(
456
+ application: ApplicationContext,
457
+ absoluteSourcePath: string,
458
+ kind: "diagram" | "vector",
459
+ maximumBytes: number,
460
+ signal: AbortSignal,
461
+ ): Promise<string> {
462
+ const lexicalRepositoryRoot = resolve(application.paths.repositoryRoot);
463
+ const privateRoot = repositoryRelativePath(
464
+ lexicalRepositoryRoot,
465
+ application.paths.privateRoot,
466
+ "Application private root",
467
+ );
468
+ const physicalPrivateRoot = await ensurePhysicalPrivateDirectoryWithin(
469
+ lexicalRepositoryRoot,
470
+ privateRoot,
471
+ );
472
+ const lexicalSourcePath = resolve(absoluteSourcePath);
473
+ const lexicalRepositorySourcePath = pathWithin(
474
+ lexicalRepositoryRoot,
475
+ lexicalSourcePath,
476
+ )
477
+ ? relative(lexicalRepositoryRoot, lexicalSourcePath)
478
+ : undefined;
479
+ const boundRepositorySource = lexicalRepositorySourcePath === undefined
480
+ ? undefined
481
+ : await bindRepositoryMedia(
482
+ application,
483
+ { path: lexicalRepositorySourcePath },
484
+ signal,
485
+ maximumBytes,
486
+ );
487
+ let physicalSource: string;
488
+ if (boundRepositorySource !== undefined) {
489
+ physicalSource = boundRepositorySource.absolutePath;
490
+ } else {
491
+ const lexicalSource = await lstat(lexicalSourcePath);
492
+ if (
493
+ lexicalSource.isSymbolicLink()
494
+ || !lexicalSource.isFile()
495
+ || lexicalSource.size < 1
496
+ || lexicalSource.size > maximumBytes
497
+ ) {
498
+ throw new ApplicationError(
499
+ "invalid-data",
500
+ "Portable operation source must be a bounded physical file.",
501
+ );
502
+ }
503
+ physicalSource = await realpath(lexicalSourcePath);
504
+ const physicalSourceDetails = await lstat(physicalSource);
505
+ if (
506
+ physicalSourceDetails.isSymbolicLink()
507
+ || !physicalSourceDetails.isFile()
508
+ || physicalSourceDetails.dev !== lexicalSource.dev
509
+ || physicalSourceDetails.ino !== lexicalSource.ino
510
+ ) {
511
+ throw new ApplicationError(
512
+ "unsafe-path",
513
+ "Portable operation source ancestry changed during physical resolution.",
514
+ );
515
+ }
516
+ }
517
+ const bundleRoot = commonPhysicalBundleRoot(
518
+ physicalSource,
519
+ physicalPrivateRoot,
520
+ );
521
+ const sourcePath = relative(bundleRoot, physicalSource);
522
+ const fileSystem = createNodeBundleFileSystem(bundleRoot);
523
+ if (
524
+ fileSystem.copyFileNoReplace === undefined
525
+ || fileSystem.inspectFile === undefined
526
+ ) {
527
+ throw new ApplicationError(
528
+ "internal",
529
+ "Portable operation input snapshots require immutable file storage.",
530
+ );
531
+ }
532
+ const sourceArtifact = boundRepositorySource !== undefined
533
+ ? boundRepositorySource.artifact
534
+ : await (async () => {
535
+ const inspected = await fileSystem.inspectFile!(sourcePath);
536
+ if (inspected.bytes < 1 || inspected.bytes > maximumBytes) {
537
+ throw new ApplicationError(
538
+ "invalid-data",
539
+ "Portable operation source exceeds its registered byte limit.",
540
+ );
541
+ }
542
+ return inspected;
543
+ })();
544
+ const extension = kind === "diagram" ? ".diagram.json" : ".image";
545
+ const absoluteSnapshotPath = resolve(physicalPrivateRoot, join(
546
+ PORTABLE_INPUT_SNAPSHOT_ROOT,
547
+ kind,
548
+ `${sourceArtifact.sha256}${extension}`,
549
+ ));
550
+ const snapshotPath = relative(bundleRoot, absoluteSnapshotPath);
551
+ if (sourcePath !== snapshotPath) {
552
+ await fileSystem.copyFileNoReplace(
553
+ sourcePath,
554
+ snapshotPath,
555
+ sourceArtifact,
556
+ );
557
+ }
558
+ const snapshotHandle = await open(
559
+ absoluteSnapshotPath,
560
+ constants.O_RDONLY | constants.O_NOFOLLOW,
561
+ );
562
+ let snapshotIdentity: Readonly<{ readonly dev: number; readonly ino: number }>;
563
+ try {
564
+ const before = await snapshotHandle.stat();
565
+ if (!before.isFile()) {
566
+ throw new ApplicationError(
567
+ "unsafe-path",
568
+ "Portable operation input snapshot must remain a physical file.",
569
+ );
570
+ }
571
+ if ((before.mode & 0o777) !== 0o400) {
572
+ await snapshotHandle.chmod(0o400);
573
+ }
574
+ const after = await snapshotHandle.stat();
575
+ if (
576
+ !after.isFile()
577
+ || after.dev !== before.dev
578
+ || after.ino !== before.ino
579
+ || (after.mode & 0o777) !== 0o400
580
+ ) {
581
+ throw new ApplicationError(
582
+ "conflict",
583
+ "Portable operation input snapshot changed while it was made read-only.",
584
+ );
585
+ }
586
+ snapshotIdentity = { dev: after.dev, ino: after.ino };
587
+ } finally {
588
+ await snapshotHandle.close();
589
+ }
590
+ const namedSnapshot = await lstat(absoluteSnapshotPath);
591
+ if (
592
+ namedSnapshot.isSymbolicLink()
593
+ || !namedSnapshot.isFile()
594
+ || namedSnapshot.dev !== snapshotIdentity.dev
595
+ || namedSnapshot.ino !== snapshotIdentity.ino
596
+ || (namedSnapshot.mode & 0o777) !== 0o400
597
+ ) {
598
+ throw new ApplicationError(
599
+ "conflict",
600
+ "Portable operation input snapshot path changed after publication.",
601
+ );
602
+ }
603
+ const snapshot = await fileSystem.inspectFile(snapshotPath);
604
+ if (
605
+ snapshot.bytes !== sourceArtifact.bytes
606
+ || snapshot.sha256 !== sourceArtifact.sha256
607
+ ) {
608
+ throw new ApplicationError(
609
+ "conflict",
610
+ "Portable operation input snapshot does not match its bound source bytes.",
611
+ );
612
+ }
613
+ return absoluteSnapshotPath;
614
+ }
615
+
616
+ async function assertPortableSnapshotIntegrity(
617
+ application: ApplicationContext,
618
+ kind: PortableSlopcameraOperationKind,
619
+ input: Readonly<Record<string, unknown>>,
620
+ signal: AbortSignal,
621
+ ): Promise<PortableSnapshotIdentity | undefined> {
622
+ const source = kind === "slopcamera.diagram.check"
623
+ || kind === "slopcamera.diagram.render"
624
+ ? input.path
625
+ : kind === "slopcamera.image.vectorize"
626
+ ? input.inputPath
627
+ : undefined;
628
+ if (typeof source !== "string") return undefined;
629
+ const lexicalRepositoryRoot = resolve(application.paths.repositoryRoot);
630
+ const lexicalPrivateRoot = resolve(application.paths.privateRoot);
631
+ if (
632
+ lexicalPrivateRoot === lexicalRepositoryRoot
633
+ || !pathWithin(lexicalRepositoryRoot, lexicalPrivateRoot)
634
+ ) return undefined;
635
+ const privateRoot = repositoryRelativePath(
636
+ lexicalRepositoryRoot,
637
+ lexicalPrivateRoot,
638
+ "Application private root",
639
+ );
640
+ const physicalRepositoryRoot = await realpath(lexicalRepositoryRoot);
641
+ const stablePhysicalPrivateRoot = resolve(
642
+ physicalRepositoryRoot,
643
+ privateRoot,
644
+ );
645
+ const resolvedSource = resolve(source);
646
+ const stableSourcePath = pathWithin(lexicalRepositoryRoot, resolvedSource)
647
+ ? resolve(
648
+ physicalRepositoryRoot,
649
+ relative(lexicalRepositoryRoot, resolvedSource),
650
+ )
651
+ : resolvedSource;
652
+ const snapshotRoot = resolve(
653
+ stablePhysicalPrivateRoot,
654
+ PORTABLE_INPUT_SNAPSHOT_ROOT,
655
+ );
656
+ const snapshotRelativePath = relative(snapshotRoot, stableSourcePath);
657
+ if (
658
+ snapshotRelativePath === ""
659
+ || snapshotRelativePath === ".."
660
+ || snapshotRelativePath.startsWith(`..${sep}`)
661
+ || isAbsolute(snapshotRelativePath)
662
+ ) return undefined;
663
+
664
+ let currentPhysicalPrivateRoot: string;
665
+ try {
666
+ currentPhysicalPrivateRoot = await realpath(application.paths.privateRoot);
667
+ } catch {
668
+ throw new ApplicationError(
669
+ "conflict",
670
+ "Portable operation private snapshot storage changed after execution planning.",
671
+ );
672
+ }
673
+ let currentPrivateRoot: Stats;
674
+ let stablePrivateRoot: Stats;
675
+ try {
676
+ [currentPrivateRoot, stablePrivateRoot] = await Promise.all([
677
+ lstat(application.paths.privateRoot),
678
+ lstat(stablePhysicalPrivateRoot),
679
+ ]);
680
+ } catch {
681
+ throw new ApplicationError(
682
+ "conflict",
683
+ "Portable operation private snapshot storage changed after execution planning.",
684
+ );
685
+ }
686
+ if (
687
+ currentPhysicalPrivateRoot !== stablePhysicalPrivateRoot
688
+ || currentPrivateRoot.isSymbolicLink()
689
+ || !currentPrivateRoot.isDirectory()
690
+ || stablePrivateRoot.isSymbolicLink()
691
+ || !stablePrivateRoot.isDirectory()
692
+ || currentPrivateRoot.dev !== stablePrivateRoot.dev
693
+ || currentPrivateRoot.ino !== stablePrivateRoot.ino
694
+ ) {
695
+ throw new ApplicationError(
696
+ "conflict",
697
+ "Portable operation private snapshot storage changed after execution planning.",
698
+ );
699
+ }
700
+
701
+ const sourceKind = kind === "slopcamera.image.vectorize"
702
+ ? "vector"
703
+ : "diagram";
704
+ const expectedSuffix = sourceKind === "diagram" ? ".diagram.json" : ".image";
705
+ const expectedPrefix = basename(source).slice(0, -expectedSuffix.length);
706
+ const expectedRelativePrefix = `${sourceKind}${sep}`;
707
+ if (
708
+ !snapshotRelativePath.startsWith(expectedRelativePrefix)
709
+ || snapshotRelativePath.slice(expectedRelativePrefix.length).includes(sep)
710
+ || !basename(source).endsWith(expectedSuffix)
711
+ || !/^[a-f0-9]{64}$/u.test(expectedPrefix)
712
+ ) {
713
+ throw new ApplicationError(
714
+ "unsafe-path",
715
+ "Portable operation input snapshot has an invalid content-addressed path.",
716
+ );
717
+ }
718
+ const repositoryPath = repositoryRelativePath(
719
+ physicalRepositoryRoot,
720
+ stableSourcePath,
721
+ "Portable operation input snapshot",
722
+ );
723
+ const before = await lstat(stableSourcePath);
724
+ if (
725
+ before.isSymbolicLink()
726
+ || !before.isFile()
727
+ || (before.mode & 0o777) !== 0o400
728
+ ) {
729
+ throw new ApplicationError(
730
+ "conflict",
731
+ "Portable operation input snapshot is no longer a read-only physical file.",
732
+ );
733
+ }
734
+ const inspected = await bindRepositoryMedia(
735
+ application,
736
+ { path: repositoryPath },
737
+ signal,
738
+ sourceKind === "diagram"
739
+ ? MAXIMUM_DIAGRAM_SOURCE_BYTES
740
+ : MAXIMUM_VECTOR_SOURCE_BYTES,
741
+ );
742
+ const after = await lstat(stableSourcePath);
743
+ if (
744
+ inspected.artifact.sha256 !== expectedPrefix
745
+ || after.isSymbolicLink()
746
+ || !after.isFile()
747
+ || after.dev !== before.dev
748
+ || after.ino !== before.ino
749
+ || (after.mode & 0o777) !== 0o400
750
+ ) {
751
+ throw new ApplicationError(
752
+ "conflict",
753
+ "Portable operation input snapshot changed after execution planning.",
754
+ {
755
+ actualSha256: inspected.artifact.sha256,
756
+ expectedSha256: expectedPrefix,
757
+ path: repositoryPath,
758
+ },
759
+ );
760
+ }
761
+ return {
762
+ bytes: inspected.artifact.bytes,
763
+ device: inspected.expectedInput.device,
764
+ inode: inspected.expectedInput.inode,
765
+ modifiedAtMs: inspected.expectedInput.modifiedAtMs,
766
+ path: stableSourcePath,
767
+ sha256: expectedPrefix,
768
+ };
769
+ }
770
+
771
+ function sameExactSnapshot(left: BigIntStats, right: BigIntStats): boolean {
772
+ return left.dev === right.dev
773
+ && left.ino === right.ino
774
+ && left.mode === right.mode
775
+ && left.nlink === right.nlink
776
+ && left.size === right.size
777
+ && left.mtimeNs === right.mtimeNs
778
+ && left.ctimeNs === right.ctimeNs;
779
+ }
780
+
781
+ async function hashOpenSnapshot(
782
+ handle: FileHandle,
783
+ bytes: number,
784
+ ): Promise<string> {
785
+ const hash = createHash("sha256");
786
+ const buffer = Buffer.allocUnsafe(1024 * 1024);
787
+ let offset = 0;
788
+ while (offset < bytes) {
789
+ const read = await handle.read(
790
+ buffer,
791
+ 0,
792
+ Math.min(buffer.byteLength, bytes - offset),
793
+ offset,
794
+ );
795
+ if (read.bytesRead === 0) break;
796
+ hash.update(buffer.subarray(0, read.bytesRead));
797
+ offset += read.bytesRead;
798
+ }
799
+ if (offset !== bytes) {
800
+ throw new ApplicationError(
801
+ "conflict",
802
+ "Portable operation input snapshot ended while its descriptor was pinned.",
803
+ );
804
+ }
805
+ return hash.digest("hex");
806
+ }
807
+
808
+ async function openPinnedPortableSnapshot(
809
+ application: ApplicationContext,
810
+ kind: PortableSlopcameraOperationKind,
811
+ input: Readonly<Record<string, unknown>>,
812
+ signal: AbortSignal,
813
+ ): Promise<PinnedPortableSnapshot | undefined> {
814
+ const expected = await assertPortableSnapshotIntegrity(
815
+ application,
816
+ kind,
817
+ input,
818
+ signal,
819
+ );
820
+ if (expected === undefined) return undefined;
821
+ if (process.platform === "win32") {
822
+ throw new ApplicationError(
823
+ "unavailable",
824
+ "Descriptor-pinned portable operation inputs require a POSIX host.",
825
+ );
826
+ }
827
+ const handle = await open(
828
+ expected.path,
829
+ constants.O_RDONLY
830
+ | (constants.O_NOFOLLOW ?? 0)
831
+ | (constants.O_NONBLOCK ?? 0),
832
+ );
833
+ try {
834
+ const [opened, exactBefore] = await Promise.all([
835
+ handle.stat(),
836
+ handle.stat({ bigint: true }),
837
+ ]);
838
+ if (
839
+ !opened.isFile()
840
+ || !exactBefore.isFile()
841
+ || opened.dev !== expected.device
842
+ || opened.ino !== expected.inode
843
+ || opened.size !== expected.bytes
844
+ || opened.mtimeMs !== expected.modifiedAtMs
845
+ || (opened.mode & 0o777) !== 0o400
846
+ || await hashOpenSnapshot(handle, expected.bytes) !== expected.sha256
847
+ ) {
848
+ throw new ApplicationError(
849
+ "conflict",
850
+ "Portable operation input snapshot changed while its descriptor was pinned.",
851
+ );
852
+ }
853
+ const descriptorPath = `/dev/fd/${String(handle.fd)}`;
854
+ const processPath = kind === "slopcamera.image.vectorize"
855
+ ? "/dev/fd/3"
856
+ : descriptorPath;
857
+ const pinnedInput = kind === "slopcamera.image.vectorize"
858
+ ? { ...input, inputPath: processPath }
859
+ : { ...input, path: processPath };
860
+ return {
861
+ assertUnchanged: async () => {
862
+ const [exactAfter, namedAfter, sha256] = await Promise.all([
863
+ handle.stat({ bigint: true }),
864
+ lstat(expected.path),
865
+ hashOpenSnapshot(handle, expected.bytes),
866
+ ]);
867
+ if (
868
+ !sameExactSnapshot(exactBefore, exactAfter)
869
+ || namedAfter.isSymbolicLink()
870
+ || !namedAfter.isFile()
871
+ || namedAfter.dev !== expected.device
872
+ || namedAfter.ino !== expected.inode
873
+ || (namedAfter.mode & 0o777) !== 0o400
874
+ || sha256 !== expected.sha256
875
+ ) {
876
+ throw new ApplicationError(
877
+ "ambiguous",
878
+ "Portable operation input snapshot changed during execution; output publication is ambiguous.",
879
+ );
880
+ }
881
+ },
882
+ close: async () => await handle.close(),
883
+ descriptor: handle.fd,
884
+ input: pinnedInput,
885
+ };
886
+ } catch (error) {
887
+ await handle.close();
888
+ throw error;
889
+ }
890
+ }
891
+
892
+ function restorePlannedDiagramSource(
893
+ kind: PortableSlopcameraOperationKind,
894
+ output: unknown,
895
+ plannedInput: Readonly<Record<string, unknown>>,
896
+ ): unknown {
897
+ if (
898
+ kind !== "slopcamera.diagram.render"
899
+ || typeof output !== "object"
900
+ || output === null
901
+ || Array.isArray(output)
902
+ ) return output;
903
+ const outputRecord = output as Readonly<Record<string, unknown>>;
904
+ const artifacts = outputRecord.artifacts;
905
+ if (
906
+ typeof artifacts !== "object"
907
+ || artifacts === null
908
+ || Array.isArray(artifacts)
909
+ || typeof plannedInput.path !== "string"
910
+ ) return output;
911
+ const artifactRecord = artifacts as Readonly<Record<string, unknown>>;
912
+ return {
913
+ ...outputRecord,
914
+ artifacts: { ...artifactRecord, spec: plannedInput.path },
915
+ };
916
+ }
917
+
918
+ /**
919
+ * Resolves portable v2 paths and pins every mutable source to an immutable,
920
+ * content-addressed private file before the scheduler hashes its exact input.
921
+ */
922
+ export async function bindSlopcameraPortableOperationInputV2(
923
+ application: ApplicationContext,
924
+ kind: PortableSlopcameraOperationKind,
925
+ inputValue: unknown,
926
+ signal: AbortSignal = new AbortController().signal,
927
+ ): Promise<unknown> {
928
+ const contract = PORTABLE_SLOPCAMERA_OPERATION_CONTRACTS[kind];
929
+ const input = contract.inputSchema.parse(inputValue);
930
+ const repositoryBoundOutput = portableOutputIsRepositoryRelative(
931
+ kind,
932
+ inputRecord(input),
933
+ );
934
+ const resolved = await canonicalizePortableOutputTarget(
935
+ kind,
936
+ resolvePortableOperationPaths(
937
+ application.paths.repositoryRoot,
938
+ kind,
939
+ input,
940
+ ),
941
+ repositoryBoundOutput ? application.paths.repositoryRoot : undefined,
942
+ );
943
+ switch (kind) {
944
+ case "slopcamera.diagram.check":
945
+ return contract.inputSchema.parse({
946
+ ...resolved,
947
+ path: await snapshotPortableSource(
948
+ application,
949
+ String(resolved.path),
950
+ "diagram",
951
+ MAXIMUM_DIAGRAM_SOURCE_BYTES,
952
+ signal,
953
+ ),
954
+ });
955
+ case "slopcamera.diagram.render": {
956
+ const sourcePath = String(resolved.path);
957
+ return contract.inputSchema.parse({
958
+ ...resolved,
959
+ // Once the source is moved into private storage, retain the original
960
+ // default output directory explicitly so rendering semantics do not
961
+ // move with it.
962
+ outDirectory: resolved.outDirectory ?? dirname(sourcePath),
963
+ path: await snapshotPortableSource(
964
+ application,
965
+ sourcePath,
966
+ "diagram",
967
+ MAXIMUM_DIAGRAM_SOURCE_BYTES,
968
+ signal,
969
+ ),
970
+ });
971
+ }
972
+ case "slopcamera.image.vectorize":
973
+ return contract.inputSchema.parse({
974
+ ...resolved,
975
+ inputPath: await snapshotPortableSource(
976
+ application,
977
+ String(resolved.inputPath),
978
+ "vector",
979
+ MAXIMUM_VECTOR_SOURCE_BYTES,
980
+ signal,
981
+ ),
982
+ });
983
+ case "slopcamera.image.generate":
984
+ return contract.inputSchema.parse(resolved);
985
+ }
986
+ }
987
+
988
+ function contractDiscovery(contract: PortableOperationContract) {
989
+ return {
990
+ inputSchemaId: contract.inputSchemaId,
991
+ kind: contract.kind,
992
+ lifecycle: contract.lifecycle,
993
+ outputSchemaId: contract.outputSchemaId,
994
+ policy: contract.policy,
995
+ version: contract.version,
996
+ } as const;
997
+ }
998
+
999
+ function hasImmutablePolicy(value: {
1000
+ readonly policy: {
1001
+ readonly preparation: readonly unknown[];
1002
+ readonly resources: readonly object[];
1003
+ };
1004
+ }): boolean {
1005
+ return Object.isFrozen(value.policy)
1006
+ && Object.isFrozen(value.policy.preparation)
1007
+ && Object.isFrozen(value.policy.resources)
1008
+ && value.policy.resources.every(resource => Object.isFrozen(resource));
1009
+ }
1010
+
1011
+ function assertPortableProjectionIntegrity(): void {
1012
+ const contractKinds = Object.keys(PORTABLE_SLOPCAMERA_OPERATION_CONTRACTS)
1013
+ .sort((left, right) => left.localeCompare(right));
1014
+ const portableKinds = [...PORTABLE_SLOPCAMERA_OPERATION_KINDS]
1015
+ .sort((left, right) => left.localeCompare(right));
1016
+ const discoveries = portableOperationContracts
1017
+ .map(contractDiscovery)
1018
+ .sort((left, right) => (
1019
+ left.kind.localeCompare(right.kind) || left.version - right.version
1020
+ ));
1021
+ if (
1022
+ PORTABLE_SLOPCAMERA_OPERATION_KINDS.length !== PORTABLE_OPERATION_COUNT
1023
+ || !isDeepStrictEqual(contractKinds, portableKinds)
1024
+ || !Object.isFrozen(PORTABLE_SLOPCAMERA_OPERATION_CONTRACTS)
1025
+ || !Object.isFrozen(PUBLIC_WORKFLOW_REGISTRY_PROJECTION)
1026
+ || !Object.isFrozen(PUBLIC_WORKFLOW_REGISTRY_PROJECTION.discovery)
1027
+ || portableOperationContracts.some(contract => (
1028
+ !Object.isFrozen(contract)
1029
+ || !hasImmutablePolicy(contract)
1030
+ ))
1031
+ || PUBLIC_WORKFLOW_REGISTRY_PROJECTION.discovery.some(operation => (
1032
+ !Object.isFrozen(operation)
1033
+ || !hasImmutablePolicy(operation)
1034
+ ))
1035
+ || !isDeepStrictEqual(
1036
+ discoveries,
1037
+ PUBLIC_WORKFLOW_REGISTRY_PROJECTION.discovery,
1038
+ )
1039
+ ) {
1040
+ throw new ApplicationError(
1041
+ "incompatible",
1042
+ "The shared portable Slopcamera contracts drifted from their public workflow projection.",
1043
+ );
1044
+ }
1045
+ }
1046
+
1047
+ async function executePortableOperation(
1048
+ context: OperationExecutionContext,
1049
+ contract: PortableOperationContract,
1050
+ input: unknown,
1051
+ dependencies: SlopcameraPortableOperationDependencies,
1052
+ ): Promise<unknown> {
1053
+ const schemaInput = contract.inputSchema.parse(input);
1054
+ const pathResolvedInput = resolvePortableOperationPaths(
1055
+ context.application.paths.repositoryRoot,
1056
+ contract.kind,
1057
+ schemaInput,
1058
+ );
1059
+ const resolvedInput = await canonicalizePortableOutputTarget(
1060
+ contract.kind,
1061
+ pathResolvedInput,
1062
+ portableOutputIsRepositoryRelative(
1063
+ contract.kind,
1064
+ inputRecord(schemaInput),
1065
+ )
1066
+ ? context.application.paths.repositoryRoot
1067
+ : undefined,
1068
+ );
1069
+ if (
1070
+ portableOutputTarget(contract.kind, pathResolvedInput)
1071
+ !== portableOutputTarget(contract.kind, resolvedInput)
1072
+ ) {
1073
+ throw new ApplicationError(
1074
+ "conflict",
1075
+ "Portable operation output target is no longer its canonical execution-planned path.",
1076
+ );
1077
+ }
1078
+ const execute = async (): Promise<unknown> => {
1079
+ const revalidatedInput = await canonicalizePortableOutputTarget(
1080
+ contract.kind,
1081
+ resolvedInput,
1082
+ );
1083
+ if (
1084
+ portableOutputTarget(contract.kind, revalidatedInput)
1085
+ !== portableOutputTarget(contract.kind, resolvedInput)
1086
+ ) {
1087
+ throw new ApplicationError(
1088
+ "conflict",
1089
+ "Portable operation output target changed before dispatch.",
1090
+ );
1091
+ }
1092
+ const pinned = await openPinnedPortableSnapshot(
1093
+ context.application,
1094
+ contract.kind,
1095
+ resolvedInput,
1096
+ context.abortSignal,
1097
+ );
1098
+ try {
1099
+ const executionInput = pinned?.input ?? resolvedInput;
1100
+ const parsedInput = (dependencies.parseInput ?? parseSlopcameraOperationInput)(
1101
+ contract.kind,
1102
+ executionInput,
1103
+ );
1104
+ const inheritedFileDescriptors = [
1105
+ ...(pinned === undefined ? [] : [pinned.descriptor]),
1106
+ ...(context.application.hostResourceLease?.inheritedFileDescriptors ?? []),
1107
+ ].filter((descriptor, index, descriptors) => (
1108
+ descriptors.indexOf(descriptor) === index
1109
+ ));
1110
+ const executionDependencies: SlopcameraOperationDependencies = {
1111
+ signal: context.abortSignal,
1112
+ ...(inheritedFileDescriptors.length === 0
1113
+ ? {}
1114
+ : { inheritedFileDescriptors }),
1115
+ };
1116
+ const output = context.application.hostResourceLease === undefined
1117
+ ? await (dependencies.execute ?? executeSlopcameraOperation)(
1118
+ contract.kind,
1119
+ parsedInput,
1120
+ executionDependencies,
1121
+ )
1122
+ : await (dependencies.executeWithLease ?? executeSlopcameraOperationWithLease)(
1123
+ contract.kind,
1124
+ parsedInput,
1125
+ context.application.hostResourceLease,
1126
+ executionDependencies,
1127
+ );
1128
+ await pinned?.assertUnchanged();
1129
+ return contract.outputSchema.parse(restorePlannedDiagramSource(
1130
+ contract.kind,
1131
+ output,
1132
+ resolvedInput,
1133
+ ));
1134
+ } finally {
1135
+ await pinned?.close();
1136
+ }
1137
+ };
1138
+ const publicationParent = slopcameraPortableOutputPublicationParent(
1139
+ contract.kind,
1140
+ resolvedInput,
1141
+ );
1142
+ return publicationParent === undefined
1143
+ ? await execute()
1144
+ : await withPortableOutputPublicationLease(
1145
+ context.application,
1146
+ publicationParent,
1147
+ execute,
1148
+ );
1149
+ }
1150
+
1151
+ function createPortableLifecycle(
1152
+ contract: PortableOperationContract,
1153
+ dependencies: SlopcameraPortableOperationDependencies,
1154
+ ): OperationLifecycle<unknown, unknown> {
1155
+ const execute = async (
1156
+ context: OperationExecutionContext,
1157
+ input: unknown,
1158
+ ): Promise<unknown> => await executePortableOperation(
1159
+ context,
1160
+ contract,
1161
+ input,
1162
+ dependencies,
1163
+ );
1164
+ return {
1165
+ execute,
1166
+ kind: contract.lifecycle,
1167
+ };
1168
+ }
1169
+
1170
+ export function createSlopcameraPortableOperationDefinitions(
1171
+ dependencies: SlopcameraPortableOperationDependencies = {},
1172
+ ): readonly OperationDefinition[] {
1173
+ assertPortableProjectionIntegrity();
1174
+ return Object.freeze(portableOperationContracts.map(contract => ({
1175
+ inputSchema: contract.inputSchema,
1176
+ inputSchemaId: contract.inputSchemaId,
1177
+ kind: contract.kind,
1178
+ lifecycle: createPortableLifecycle(contract, dependencies),
1179
+ outputSchema: contract.outputSchema,
1180
+ outputSchemaId: contract.outputSchemaId,
1181
+ policy: contract.policy,
1182
+ summarize: () => ({ fields: {}, kind: contract.kind }),
1183
+ version: contract.version,
1184
+ })));
1185
+ }
1186
+
1187
+ export const slopcameraPortableOperationDefinitions =
1188
+ createSlopcameraPortableOperationDefinitions();