@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,584 @@
1
+ import { constants } from "node:fs";
2
+ import {
3
+ lstat,
4
+ mkdir,
5
+ open,
6
+ realpath,
7
+ } from "node:fs/promises";
8
+ import {
9
+ basename,
10
+ dirname,
11
+ extname,
12
+ isAbsolute,
13
+ relative,
14
+ resolve,
15
+ } from "node:path";
16
+
17
+ import { z } from "zod";
18
+
19
+ import type { ApplicationContext } from "../application/context";
20
+ import { ApplicationError } from "../application/errors";
21
+ import type {
22
+ OperationDescription,
23
+ OperationDiscovery,
24
+ OperationRegistry,
25
+ } from "../application/registry";
26
+ import type { GraphPlanV1 } from "../code/contracts";
27
+ import {
28
+ TRUSTED_CODE_WARNING,
29
+ planBuiltInWorkflow,
30
+ planCodeWorkflow,
31
+ prepareCodeWorkflowRun,
32
+ } from "../code/planning";
33
+ import { checkAndBundleWorkflowSource } from "../code/source-bundle";
34
+ import type { BuiltInWorkflow } from "../workflows";
35
+
36
+ const MAXIMUM_WORKFLOW_INPUT_BYTES = 4 * 1024 * 1024;
37
+
38
+ export interface WorkflowPlanSummary {
39
+ readonly bounds: GraphPlanV1["envelope"]["bounds"];
40
+ readonly computeKeys: GraphPlanV1["envelope"]["computeKeys"];
41
+ readonly effects: GraphPlanV1["envelope"]["effects"];
42
+ readonly graphPlanSha256: string;
43
+ readonly initialSubjects: GraphPlanV1["staticBindings"]["initialSubjects"];
44
+ readonly operationKinds: GraphPlanV1["envelope"]["operationKinds"];
45
+ readonly outputs: GraphPlanV1["graph"]["outputs"];
46
+ readonly preparation: GraphPlanV1["envelope"]["preparation"];
47
+ readonly resources: GraphPlanV1["envelope"]["resources"];
48
+ readonly topologicalWaves: GraphPlanV1["topologicalWaves"];
49
+ readonly unresolved: GraphPlanV1["envelope"]["unresolved"];
50
+ readonly warning: string;
51
+ readonly workflow: {
52
+ readonly id: string;
53
+ readonly version: number;
54
+ };
55
+ }
56
+
57
+ export interface CheckedWorkflowSource {
58
+ readonly bundleSha256: string;
59
+ readonly bytes: number;
60
+ readonly dependencyGraphSha256: string;
61
+ readonly entrypoint: string;
62
+ readonly importedModules: readonly string[];
63
+ readonly runtime: {
64
+ readonly bunRevision: string;
65
+ readonly bunVersion: string;
66
+ readonly format: "esm";
67
+ readonly target: "bun";
68
+ };
69
+ readonly sourceSha256: string;
70
+ readonly warning: string;
71
+ }
72
+
73
+ function isWithin(root: string, candidate: string): boolean {
74
+ const fromRoot = relative(root, candidate);
75
+ return fromRoot === "" || (!fromRoot.startsWith("..") && !isAbsolute(fromRoot));
76
+ }
77
+
78
+ function missing(error: unknown): boolean {
79
+ return error instanceof Error
80
+ && "code" in error
81
+ && error.code === "ENOENT";
82
+ }
83
+
84
+ async function physicalRepositoryRoot(repositoryRoot: string): Promise<{
85
+ readonly lexical: string;
86
+ readonly physical: string;
87
+ }> {
88
+ const lexical = resolve(repositoryRoot);
89
+ const details = await lstat(lexical);
90
+ if (!details.isDirectory()) {
91
+ throw new ApplicationError("unsafe-path", "The repository root must be a directory.");
92
+ }
93
+ return {
94
+ lexical,
95
+ physical: await realpath(lexical),
96
+ };
97
+ }
98
+
99
+ function repositoryRelativePath(
100
+ lexicalRoot: string,
101
+ requested: string,
102
+ ): { readonly absolute: string; readonly relativePath: string } {
103
+ const absolute = resolve(lexicalRoot, requested);
104
+ const relativePath = relative(lexicalRoot, absolute);
105
+ if (
106
+ relativePath === ""
107
+ || relativePath.startsWith("..")
108
+ || isAbsolute(relativePath)
109
+ ) {
110
+ throw new ApplicationError(
111
+ "unsafe-path",
112
+ `Path must name a file inside the repository: ${requested}`,
113
+ );
114
+ }
115
+ return { absolute, relativePath };
116
+ }
117
+
118
+ async function physicalRepositoryFile(
119
+ repositoryRoot: string,
120
+ requested: string,
121
+ maximumBytes: number,
122
+ ): Promise<{
123
+ readonly handle: Awaited<ReturnType<typeof open>>;
124
+ readonly path: string;
125
+ readonly size: number;
126
+ }> {
127
+ const root = await physicalRepositoryRoot(repositoryRoot);
128
+ const candidate = repositoryRelativePath(root.lexical, requested);
129
+ const expectedPhysical = resolve(root.physical, candidate.relativePath);
130
+ const details = await lstat(candidate.absolute);
131
+ if (details.isSymbolicLink() || !details.isFile()) {
132
+ throw new ApplicationError(
133
+ "unsafe-path",
134
+ `Input must be a physical regular file: ${requested}`,
135
+ );
136
+ }
137
+ const physical = await realpath(candidate.absolute);
138
+ if (
139
+ physical !== expectedPhysical
140
+ || !isWithin(root.physical, physical)
141
+ ) {
142
+ throw new ApplicationError(
143
+ "unsafe-path",
144
+ `Input crosses a symbolic link outside its repository path: ${requested}`,
145
+ );
146
+ }
147
+ if (details.size > maximumBytes) {
148
+ throw new ApplicationError(
149
+ "invalid-data",
150
+ `Input exceeds ${String(maximumBytes)} bytes: ${requested}`,
151
+ );
152
+ }
153
+ const handle = await open(
154
+ physical,
155
+ constants.O_RDONLY | (constants.O_NOFOLLOW ?? 0),
156
+ );
157
+ const opened = await handle.stat();
158
+ if (
159
+ !opened.isFile()
160
+ || opened.dev !== details.dev
161
+ || opened.ino !== details.ino
162
+ || opened.size !== details.size
163
+ ) {
164
+ await handle.close();
165
+ throw new ApplicationError(
166
+ "conflict",
167
+ `Input changed while it was being opened: ${requested}`,
168
+ );
169
+ }
170
+ return { handle, path: physical, size: opened.size };
171
+ }
172
+
173
+ export async function readWorkflowInput(
174
+ application: ApplicationContext,
175
+ requested: string,
176
+ ): Promise<unknown> {
177
+ const opened = await physicalRepositoryFile(
178
+ application.paths.repositoryRoot,
179
+ requested,
180
+ MAXIMUM_WORKFLOW_INPUT_BYTES,
181
+ );
182
+ try {
183
+ const bytes = await opened.handle.readFile();
184
+ const final = await opened.handle.stat();
185
+ if (final.size !== opened.size || bytes.byteLength !== opened.size) {
186
+ throw new ApplicationError(
187
+ "conflict",
188
+ `Workflow input changed while it was being read: ${requested}`,
189
+ );
190
+ }
191
+ let text: string;
192
+ try {
193
+ text = new TextDecoder("utf-8", { fatal: true }).decode(bytes);
194
+ } catch {
195
+ throw new ApplicationError(
196
+ "invalid-data",
197
+ `Workflow input is not valid UTF-8: ${requested}`,
198
+ );
199
+ }
200
+ try {
201
+ return JSON.parse(text) as unknown;
202
+ } catch (error) {
203
+ throw new ApplicationError(
204
+ "invalid-data",
205
+ `Workflow input is not valid JSON: ${requested}`,
206
+ { reason: String(error) },
207
+ );
208
+ }
209
+ } finally {
210
+ await opened.handle.close();
211
+ }
212
+ }
213
+
214
+ export function workflowPlanSummary(plan: GraphPlanV1): WorkflowPlanSummary {
215
+ return {
216
+ bounds: plan.envelope.bounds,
217
+ computeKeys: plan.envelope.computeKeys,
218
+ effects: plan.envelope.effects,
219
+ graphPlanSha256: plan.graphPlanSha256,
220
+ initialSubjects: plan.staticBindings.initialSubjects,
221
+ operationKinds: plan.envelope.operationKinds,
222
+ outputs: plan.graph.outputs,
223
+ preparation: plan.envelope.preparation,
224
+ resources: plan.envelope.resources,
225
+ topologicalWaves: plan.topologicalWaves,
226
+ unresolved: plan.envelope.unresolved,
227
+ warning: TRUSTED_CODE_WARNING,
228
+ workflow: {
229
+ id: plan.graph.workflow.id,
230
+ version: plan.graph.workflow.version,
231
+ },
232
+ };
233
+ }
234
+
235
+ export function humanWorkflowPlan(summary: WorkflowPlanSummary): string {
236
+ const waves = summary.topologicalWaves
237
+ .map((wave, index) => `wave ${String(index + 1)} ${wave.join(" ")}`)
238
+ .join("\n");
239
+ return [
240
+ `${summary.workflow.id}@${String(summary.workflow.version)} ${summary.graphPlanSha256}`,
241
+ `${String(summary.bounds.nodes)} nodes, ${String(summary.topologicalWaves.length)} waves`,
242
+ `operations ${summary.operationKinds.join(" ") || "none"}`,
243
+ `compute ${summary.computeKeys.join(" ") || "none"}`,
244
+ `unresolved ${summary.unresolved.join(" ") || "none"}`,
245
+ waves,
246
+ `warning ${summary.warning}`,
247
+ ].filter(line => line !== "").join("\n");
248
+ }
249
+
250
+ export function operationDiscoveryList(
251
+ registry: OperationRegistry,
252
+ ): readonly OperationDiscovery[] {
253
+ return registry.list();
254
+ }
255
+
256
+ export function operationDiscovery(
257
+ registry: OperationRegistry,
258
+ requested: string,
259
+ ): OperationDescription {
260
+ const matches = registry.list().filter(operation => (
261
+ operation.kind === requested
262
+ || `${operation.kind}@${String(operation.version)}` === requested
263
+ ));
264
+ if (matches.length === 0) {
265
+ throw new ApplicationError("not-found", `Unknown registered operation: ${requested}`);
266
+ }
267
+ if (matches.length > 1) {
268
+ throw new ApplicationError(
269
+ "conflict",
270
+ `Operation ${requested} has multiple versions; use <kind>@<version>.`,
271
+ );
272
+ }
273
+ const match = matches[0]!;
274
+ return registry.describe(match.kind, match.version);
275
+ }
276
+
277
+ export function humanOperation(operation: OperationDiscovery): string {
278
+ return [
279
+ `${operation.kind}@${String(operation.version)}`,
280
+ `schemas ${operation.inputSchemaId} -> ${operation.outputSchemaId}`,
281
+ `lifecycle ${operation.lifecycle}`,
282
+ `effect ${operation.policy.effect}; resume ${operation.policy.resume}; cache ${operation.policy.cache}`,
283
+ `resources ${operation.policy.resources.map(claim => `${claim.resource}:${String(claim.amount)}`).join(" ") || "none"}`,
284
+ `preparation ${operation.policy.preparation.join(" ") || "none"}`,
285
+ `bounds input=${String(operation.policy.maxInputBytes)} output=${String(operation.policy.maxOutputBytes)} durationMs=${String(operation.policy.maxDurationMs)} fanOut=${String(operation.policy.maxFanOut)}`,
286
+ ].join("\n");
287
+ }
288
+
289
+ export function humanOperationList(
290
+ operations: readonly OperationDiscovery[],
291
+ ): string {
292
+ return operations.map(operation => (
293
+ `${operation.kind}@${String(operation.version)}\t${operation.policy.effect}\t${operation.lifecycle}`
294
+ )).join("\n");
295
+ }
296
+
297
+ export function workflowCatalogEntry(workflow: BuiltInWorkflow) {
298
+ return {
299
+ description: workflow.description,
300
+ id: workflow.id,
301
+ inputSchemaId: workflow.inputSchemaId,
302
+ title: workflow.title,
303
+ version: workflow.version,
304
+ } as const;
305
+ }
306
+
307
+ export function workflowCatalogDescription(workflow: BuiltInWorkflow) {
308
+ return {
309
+ ...workflowCatalogEntry(workflow),
310
+ inputJsonSchema: {
311
+ ...z.toJSONSchema(workflow.inputSchema),
312
+ $id: workflow.inputSchemaId,
313
+ },
314
+ } as const;
315
+ }
316
+
317
+ export function humanWorkflow(workflow: BuiltInWorkflow): string {
318
+ return [
319
+ `${workflow.id}@${String(workflow.version)} — ${workflow.title}`,
320
+ workflow.description,
321
+ `input ${workflow.inputSchemaId}`,
322
+ ].join("\n");
323
+ }
324
+
325
+ export async function planCustomWorkflow(options: {
326
+ readonly application: ApplicationContext;
327
+ readonly inputPath: string;
328
+ readonly registry: OperationRegistry;
329
+ readonly sourcePath: string;
330
+ }) {
331
+ const workflowInput = await readWorkflowInput(
332
+ options.application,
333
+ options.inputPath,
334
+ );
335
+ return await planCodeWorkflow({
336
+ application: options.application,
337
+ entryPath: resolve(
338
+ options.application.paths.repositoryRoot,
339
+ options.sourcePath,
340
+ ),
341
+ registry: options.registry,
342
+ workflowInput,
343
+ });
344
+ }
345
+
346
+ export async function prepareCustomWorkflowRun(options: {
347
+ readonly application: ApplicationContext;
348
+ readonly inputPath: string;
349
+ readonly registry: OperationRegistry;
350
+ readonly sourcePath: string;
351
+ }) {
352
+ const workflowInput = await readWorkflowInput(
353
+ options.application,
354
+ options.inputPath,
355
+ );
356
+ return await prepareCodeWorkflowRun({
357
+ application: options.application,
358
+ entryPath: resolve(
359
+ options.application.paths.repositoryRoot,
360
+ options.sourcePath,
361
+ ),
362
+ registry: options.registry,
363
+ workflowInput,
364
+ });
365
+ }
366
+
367
+ export async function planCatalogWorkflow(options: {
368
+ readonly application: ApplicationContext;
369
+ readonly inputPath: string;
370
+ readonly registry: OperationRegistry;
371
+ readonly workflow: BuiltInWorkflow;
372
+ }) {
373
+ return await planBuiltInWorkflow({
374
+ application: options.application,
375
+ registry: options.registry,
376
+ workflow: options.workflow,
377
+ workflowInput: await readWorkflowInput(
378
+ options.application,
379
+ options.inputPath,
380
+ ),
381
+ });
382
+ }
383
+
384
+ export async function checkCustomWorkflow(options: {
385
+ readonly application: ApplicationContext;
386
+ readonly sourcePath: string;
387
+ }): Promise<CheckedWorkflowSource> {
388
+ const bundle = await checkAndBundleWorkflowSource({
389
+ allowedRoot: options.application.paths.repositoryRoot,
390
+ entryPath: resolve(
391
+ options.application.paths.repositoryRoot,
392
+ options.sourcePath,
393
+ ),
394
+ ...(options.application.hostResourceLease === undefined
395
+ ? {}
396
+ : {
397
+ inheritedHostResourceFileDescriptor:
398
+ options.application.hostResourceLease.inheritedFileDescriptor,
399
+ }),
400
+ });
401
+ return {
402
+ bundleSha256: bundle.sha256,
403
+ bytes: bundle.bytes.byteLength,
404
+ dependencyGraphSha256: bundle.dependencyGraphSha256,
405
+ entrypoint: bundle.entryRelativePath,
406
+ importedModules: bundle.importedPaths,
407
+ runtime: {
408
+ bunRevision: bundle.identity.bunRevision,
409
+ bunVersion: bundle.identity.bunVersion,
410
+ format: bundle.identity.format,
411
+ target: bundle.identity.target,
412
+ },
413
+ sourceSha256: bundle.sourceSha256,
414
+ warning: TRUSTED_CODE_WARNING,
415
+ };
416
+ }
417
+
418
+ function workflowIdForPath(path: string): string {
419
+ const withoutExtension = basename(path, extname(path))
420
+ .toLowerCase()
421
+ .replaceAll(/[^a-z0-9]+/gu, "-")
422
+ .replaceAll(/^-+|-+$/gu, "");
423
+ return /^[a-z]/u.test(withoutExtension)
424
+ ? withoutExtension.slice(0, 128)
425
+ : `workflow-${withoutExtension || "custom"}`.slice(0, 128);
426
+ }
427
+
428
+ function workflowTemplate(path: string): string {
429
+ const id = workflowIdForPath(path);
430
+ const defaultOutput = `renders/${id}/final.mp4`;
431
+ return `import { z } from "zod";
432
+ import { defineWorkflow } from "@hraness/slopcamera/local/code";
433
+
434
+ const EvenDimensionSchema = z.number()
435
+ .int()
436
+ .safe()
437
+ .positive()
438
+ .max(16_384)
439
+ .refine(value => value % 2 === 0, "H.264 output dimensions must be even.");
440
+
441
+ const RenderSchema = z.strictObject({
442
+ background: z.string()
443
+ .regex(/^#[a-fA-F0-9]{6}(?:[a-fA-F0-9]{2})?$/u)
444
+ .default("#000000"),
445
+ frameRate: z.number().finite().positive().max(240).default(30),
446
+ maximumBytes: z.number()
447
+ .int()
448
+ .safe()
449
+ .positive()
450
+ .max(1_099_511_627_776)
451
+ .default(8 * 1_024 * 1_024 * 1_024),
452
+ output: z.string()
453
+ .min(12)
454
+ .max(512)
455
+ .regex(
456
+ /^renders\\/(?:[A-Za-z0-9_-]+\\/)*[A-Za-z0-9_-]+\\.mp4$/u,
457
+ "Expected a lowercase .mp4 path below renders/.",
458
+ )
459
+ .default(${JSON.stringify(defaultOutput)}),
460
+ pixelHeight: EvenDimensionSchema.default(1_080),
461
+ pixelWidth: EvenDimensionSchema.default(1_920),
462
+ syncPolicy: z.enum(["require-verified", "allow-unverified"])
463
+ .default("require-verified"),
464
+ }).prefault({});
465
+
466
+ const InputSchema = z.strictObject({
467
+ project: z.string().regex(/^project_[A-Za-z0-9][A-Za-z0-9_-]*$/u),
468
+ render: RenderSchema,
469
+ });
470
+
471
+ export default defineWorkflow({
472
+ id: ${JSON.stringify(id)},
473
+ inputSchema: InputSchema,
474
+ inputSchemaId: ${JSON.stringify(`slopcamera.workflow.${id}.input/v1`)},
475
+ version: 1,
476
+ build(workflow, input) {
477
+ const project = workflow.project.snapshot("project", input.project);
478
+ const revision = workflow.project.freezeRevision("revision", {
479
+ pixelHeight: input.render.pixelHeight,
480
+ pixelWidth: input.render.pixelWidth,
481
+ project,
482
+ });
483
+ const renderPlan = workflow.render.plan("render-plan", {
484
+ revision,
485
+ settings: {
486
+ background: input.render.background,
487
+ frameRate: input.render.frameRate,
488
+ },
489
+ });
490
+ const render = workflow.render.project("render", {
491
+ output: {
492
+ maximumBytes: input.render.maximumBytes,
493
+ path: input.render.output,
494
+ },
495
+ plan: renderPlan,
496
+ syncPolicy: input.render.syncPolicy,
497
+ });
498
+ return { render, renderPlan, revision: revision.revision };
499
+ },
500
+ });
501
+ `;
502
+ }
503
+
504
+ async function ensurePhysicalParentWithinRepository(
505
+ repositoryRoot: string,
506
+ requested: string,
507
+ ): Promise<string> {
508
+ const root = await physicalRepositoryRoot(repositoryRoot);
509
+ const candidate = repositoryRelativePath(root.lexical, requested);
510
+ const segments = relative(root.lexical, dirname(candidate.absolute))
511
+ .split(/[\\/]/u)
512
+ .filter(segment => segment !== "" && segment !== ".");
513
+ let lexicalDirectory = root.lexical;
514
+ let physicalDirectory = root.physical;
515
+ for (const segment of segments) {
516
+ lexicalDirectory = resolve(lexicalDirectory, segment);
517
+ physicalDirectory = resolve(physicalDirectory, segment);
518
+ try {
519
+ const details = await lstat(lexicalDirectory);
520
+ if (details.isSymbolicLink() || !details.isDirectory()) {
521
+ throw new ApplicationError(
522
+ "unsafe-path",
523
+ `Workflow parent must contain only physical directories: ${requested}`,
524
+ );
525
+ }
526
+ } catch (error) {
527
+ if (!missing(error)) throw error;
528
+ await mkdir(lexicalDirectory, { mode: 0o700 });
529
+ }
530
+ if (await realpath(lexicalDirectory) !== physicalDirectory) {
531
+ throw new ApplicationError(
532
+ "unsafe-path",
533
+ `Workflow parent crosses a symbolic link: ${requested}`,
534
+ );
535
+ }
536
+ }
537
+ return candidate.absolute;
538
+ }
539
+
540
+ export async function initializeWorkflowSource(
541
+ application: ApplicationContext,
542
+ requested: string,
543
+ ): Promise<{ readonly bytes: number; readonly path: string }> {
544
+ const path = await ensurePhysicalParentWithinRepository(
545
+ application.paths.repositoryRoot,
546
+ requested,
547
+ );
548
+ if (![".ts", ".tsx", ".mts", ".cts"].includes(extname(path).toLowerCase())) {
549
+ throw new ApplicationError(
550
+ "usage",
551
+ "Workflow source should use a TypeScript extension (.ts, .tsx, .mts, or .cts).",
552
+ );
553
+ }
554
+ const source = workflowTemplate(path);
555
+ let handle: Awaited<ReturnType<typeof open>>;
556
+ try {
557
+ handle = await open(
558
+ path,
559
+ constants.O_WRONLY
560
+ | constants.O_CREAT
561
+ | constants.O_EXCL
562
+ | (constants.O_NOFOLLOW ?? 0),
563
+ 0o600,
564
+ );
565
+ } catch (error) {
566
+ if (error instanceof Error && "code" in error && error.code === "EEXIST") {
567
+ throw new ApplicationError(
568
+ "conflict",
569
+ `Workflow source already exists; code init never overwrites: ${requested}`,
570
+ );
571
+ }
572
+ throw error;
573
+ }
574
+ try {
575
+ await handle.writeFile(source, { encoding: "utf8" });
576
+ await handle.sync();
577
+ } finally {
578
+ await handle.close();
579
+ }
580
+ return {
581
+ bytes: Buffer.byteLength(source),
582
+ path: relative(application.paths.repositoryRoot, path),
583
+ };
584
+ }