@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,797 @@
1
+ import { AsyncLocalStorage } from "node:async_hooks"
2
+ import { constants } from "node:fs"
3
+ import {
4
+ lstat,
5
+ mkdtemp,
6
+ open,
7
+ rmdir,
8
+ unlink,
9
+ type FileHandle,
10
+ } from "node:fs/promises"
11
+ import { join } from "node:path"
12
+ import { nonGatewayChildEnvironment } from "../process-environment.js"
13
+ import { VectorizeError, type VectorizeErrorCode } from "./types.js"
14
+
15
+ const MAX_COMMAND_OUTPUT_BYTES = 64 * 1_024
16
+ const PRIMARY_OUTPUT_READ_BYTES = 64 * 1_024
17
+ const PRIMARY_OUTPUT_POLL_MS = 2
18
+ const PRIMARY_OUTPUT_EXIT_EMPTY_POLLS = 2
19
+ const TERMINATION_GRACE_MS = 50
20
+ const HARD_KILL_WAIT_MS = 500
21
+ const MAX_INHERITED_FILE_DESCRIPTORS = 16
22
+
23
+ const timeoutMarker = Symbol("bounded-command-timeout")
24
+ const noCommandFailure = Symbol("no-command-failure")
25
+ const activePosixProcessGroups = new Set<number>()
26
+ let workerTerminationForwardingInstalled = false
27
+ const inheritedCommandFileDescriptors = new AsyncLocalStorage<readonly number[]>()
28
+
29
+ export interface CommandResult {
30
+ readonly stderr: string
31
+ readonly stdout: string
32
+ }
33
+
34
+ export interface BoundedCommandOptions {
35
+ /**
36
+ * Open descriptors explicitly duplicated into the child from descriptor 3.
37
+ * Values are local execution authority and never enter receipts or logs.
38
+ */
39
+ readonly inheritedFileDescriptors?: readonly number[]
40
+ /** Bounded bytes delivered to the child on standard input. */
41
+ readonly stdin?: Uint8Array
42
+ /** Raise the stdout quota only for a caller that consumes bounded data there. */
43
+ readonly maxStdoutBytes?: number
44
+ }
45
+
46
+ function normalizedInheritedFileDescriptors(
47
+ value: readonly number[] | undefined,
48
+ ): readonly number[] {
49
+ const descriptors = value ?? []
50
+ if (
51
+ descriptors.length > MAX_INHERITED_FILE_DESCRIPTORS
52
+ || descriptors.some((descriptor, index) => (
53
+ !Number.isSafeInteger(descriptor)
54
+ || descriptor < 0
55
+ || descriptor > 2_147_483_647
56
+ || descriptors.indexOf(descriptor) !== index
57
+ ))
58
+ ) {
59
+ throw new VectorizeError(
60
+ "invalid_input",
61
+ "Inherited vectorizer descriptors must be unique bounded integers.",
62
+ )
63
+ }
64
+ return Object.freeze([...descriptors])
65
+ }
66
+
67
+ export async function withInheritedCommandFileDescriptors<T>(
68
+ descriptors: readonly number[] | undefined,
69
+ callback: () => T | Promise<T>,
70
+ ): Promise<T> {
71
+ const normalized = normalizedInheritedFileDescriptors(descriptors)
72
+ return await inheritedCommandFileDescriptors.run(
73
+ normalized,
74
+ async () => await callback(),
75
+ )
76
+ }
77
+
78
+ export interface BoundedPathOutputOptions extends BoundedCommandOptions {
79
+ /** Maximum bytes accepted from the command's pathname-based primary output. */
80
+ readonly maxOutputBytes: number
81
+ /** Existing private directory in which to create the per-call output endpoint. */
82
+ readonly temporaryRoot: string
83
+ }
84
+
85
+ export interface PathOutputCommandResult extends CommandResult {
86
+ readonly output: string
87
+ }
88
+
89
+ interface ManagedSubprocess {
90
+ readonly exitCode: number | null
91
+ readonly exited: Promise<number>
92
+ kill(signal?: number | NodeJS.Signals): void
93
+ readonly pid: number
94
+ readonly stderr: ReadableStream<Uint8Array>
95
+ readonly stdout: ReadableStream<Uint8Array>
96
+ }
97
+
98
+ /**
99
+ * Forward supervisor termination into every command group owned by the
100
+ * isolated worker. This is process-local and called only by the worker
101
+ * entrypoint before conversion begins.
102
+ */
103
+ export function forwardVectorizeWorkerTermination(): void {
104
+ if (
105
+ process.platform === "win32" ||
106
+ workerTerminationForwardingInstalled
107
+ ) {
108
+ return
109
+ }
110
+ workerTerminationForwardingInstalled = true
111
+ const signals = [
112
+ ["SIGHUP", 129],
113
+ ["SIGINT", 130],
114
+ ["SIGTERM", 143],
115
+ ] as const
116
+ for (const [signal, exitCode] of signals) {
117
+ process.once(signal, () => {
118
+ for (const pid of activePosixProcessGroups) {
119
+ safelyKillPosixProcessGroupByPid(pid, "SIGKILL")
120
+ }
121
+ process.exit(exitCode)
122
+ })
123
+ }
124
+ }
125
+
126
+ export async function runBoundedCommand(
127
+ command: readonly string[],
128
+ timeoutMs: number,
129
+ failureCode: VectorizeErrorCode,
130
+ options: BoundedCommandOptions = {},
131
+ ): Promise<CommandResult> {
132
+ return runBoundedCommandInternal(command, timeoutMs, failureCode, options)
133
+ }
134
+
135
+ /**
136
+ * Run a command whose primary output must be a pathname while retaining a
137
+ * bounded streaming boundary. A private FIFO gives pathname-only tools a
138
+ * portable output argument and applies backpressure before output can exceed
139
+ * the in-memory quota.
140
+ */
141
+ export async function runBoundedPathOutputCommand(
142
+ commandForOutput: (outputPath: string) => readonly string[],
143
+ timeoutMs: number,
144
+ failureCode: VectorizeErrorCode,
145
+ options: BoundedPathOutputOptions,
146
+ ): Promise<PathOutputCommandResult> {
147
+ if (process.platform === "win32") {
148
+ throw new VectorizeError(
149
+ "tool_platform",
150
+ "Bounded pathname output is unavailable on Windows.",
151
+ { platform: process.platform },
152
+ )
153
+ }
154
+ assertPositiveLimit(
155
+ options.maxOutputBytes,
156
+ "The command primary-output limit must be positive.",
157
+ )
158
+ if (timeoutMs < 1) {
159
+ throw new VectorizeError("timeout", "VTracer exceeded the conversion time limit.")
160
+ }
161
+
162
+ const startedAt = performance.now()
163
+ let anchorHandle: FileHandle | undefined
164
+ let directoryHandle: FileHandle | undefined
165
+ let outputDirectory: string | undefined
166
+ let readerHandle: FileHandle | undefined
167
+ let result: PathOutputCommandResult | undefined
168
+ let failure: unknown | typeof noCommandFailure = noCommandFailure
169
+ try {
170
+ outputDirectory = await mkdtemp(
171
+ join(options.temporaryRoot, "slopcamera-command-output-"),
172
+ )
173
+ directoryHandle = await open(
174
+ outputDirectory,
175
+ constants.O_RDONLY | constants.O_DIRECTORY | constants.O_NOFOLLOW,
176
+ )
177
+ await assertOpenedPrivateDirectory(outputDirectory, directoryHandle)
178
+ const outputPath = join(outputDirectory, "output.svg")
179
+ await createPrivateFifo(
180
+ outputPath,
181
+ remainingCommandTime(startedAt, timeoutMs),
182
+ failureCode,
183
+ )
184
+ anchorHandle = await open(
185
+ outputPath,
186
+ constants.O_RDWR | constants.O_NONBLOCK | constants.O_NOFOLLOW,
187
+ )
188
+ readerHandle = await open(
189
+ outputPath,
190
+ constants.O_RDONLY | constants.O_NONBLOCK | constants.O_NOFOLLOW,
191
+ )
192
+ await assertOpenedPrivateFifo(outputPath, anchorHandle, readerHandle)
193
+ // VTracer only needs to traverse this directory and open the FIFO. Removing
194
+ // directory write permission prevents accidental replacement of the path
195
+ // between verification and command startup.
196
+ await directoryHandle.chmod(0o500)
197
+
198
+ const commandResult = await runBoundedCommandInternal(
199
+ commandForOutput(outputPath),
200
+ remainingCommandTime(startedAt, timeoutMs),
201
+ failureCode,
202
+ options,
203
+ {
204
+ handle: readerHandle,
205
+ maximumBytes: options.maxOutputBytes,
206
+ },
207
+ )
208
+ if (commandResult.primaryOutput === undefined) {
209
+ throw new VectorizeError(
210
+ failureCode,
211
+ "VTracer did not expose its bounded primary output.",
212
+ )
213
+ }
214
+ result = {
215
+ output: commandResult.primaryOutput,
216
+ stderr: commandResult.stderr,
217
+ stdout: commandResult.stdout,
218
+ }
219
+ } catch (error) {
220
+ failure =
221
+ error instanceof VectorizeError
222
+ ? error
223
+ : executionError(failureCode, error)
224
+ }
225
+
226
+ const cleanupError = await removePrimaryOutputEndpoint(
227
+ outputDirectory,
228
+ outputDirectory === undefined
229
+ ? undefined
230
+ : join(outputDirectory, "output.svg"),
231
+ readerHandle,
232
+ anchorHandle,
233
+ directoryHandle,
234
+ )
235
+ if (failure !== noCommandFailure) {
236
+ if (cleanupError === undefined) throw failure
237
+ throw new VectorizeError(
238
+ failure instanceof VectorizeError ? failure.code : failureCode,
239
+ `${failure instanceof Error ? failure.message : String(failure)} The temporary primary-output endpoint also could not be removed.`,
240
+ {
241
+ ...(failure instanceof VectorizeError ? failure.details : {}),
242
+ cleanup: String(cleanupError),
243
+ },
244
+ { cause: failure instanceof Error ? failure : undefined },
245
+ )
246
+ }
247
+ if (cleanupError !== undefined) {
248
+ throw new VectorizeError(
249
+ failureCode,
250
+ "The temporary primary-output endpoint could not be removed.",
251
+ { cleanup: String(cleanupError) },
252
+ { cause: cleanupError instanceof Error ? cleanupError : undefined },
253
+ )
254
+ }
255
+ if (result === undefined) {
256
+ throw new VectorizeError(
257
+ failureCode,
258
+ "The bounded command completed without a result.",
259
+ )
260
+ }
261
+ return result
262
+ }
263
+
264
+ interface PrimaryOutputReader {
265
+ readonly handle: FileHandle
266
+ readonly maximumBytes: number
267
+ }
268
+
269
+ interface InternalCommandResult extends CommandResult {
270
+ readonly primaryOutput?: string
271
+ }
272
+
273
+ async function runBoundedCommandInternal(
274
+ command: readonly string[],
275
+ timeoutMs: number,
276
+ failureCode: VectorizeErrorCode,
277
+ options: BoundedCommandOptions,
278
+ primaryOutput?: PrimaryOutputReader,
279
+ ): Promise<InternalCommandResult> {
280
+ if (command.length === 0) {
281
+ throw new VectorizeError("invalid_input", "A bounded command requires a command.")
282
+ }
283
+ if (timeoutMs < 1) {
284
+ throw new VectorizeError("timeout", "VTracer exceeded the conversion time limit.")
285
+ }
286
+ const maxStdoutBytes = options.maxStdoutBytes ?? MAX_COMMAND_OUTPUT_BYTES
287
+ assertPositiveLimit(maxStdoutBytes, "The command stdout limit must be positive.")
288
+ const inheritedDescriptors = normalizedInheritedFileDescriptors(
289
+ options.inheritedFileDescriptors
290
+ ?? inheritedCommandFileDescriptors.getStore(),
291
+ )
292
+
293
+ let child: ManagedSubprocess
294
+ const ownsProcessGroup = process.platform !== "win32"
295
+ try {
296
+ child = Bun.spawn([...command], {
297
+ detached: ownsProcessGroup,
298
+ env: nonGatewayChildEnvironment(),
299
+ stdio: [
300
+ options.stdin ?? "ignore",
301
+ "pipe",
302
+ "pipe",
303
+ ...inheritedDescriptors,
304
+ ],
305
+ windowsHide: true,
306
+ })
307
+ } catch (error) {
308
+ throw executionError(failureCode, error)
309
+ }
310
+ if (process.platform !== "win32") {
311
+ activePosixProcessGroups.add(child.pid)
312
+ }
313
+
314
+ const streamAbort = new AbortController()
315
+ let childHasExited = false
316
+ const childExitTask = child.exited.then((exitCode) => {
317
+ childHasExited = true
318
+ if (ownsProcessGroup && process.platform !== "win32") {
319
+ // A leader can exit while background descendants still own its pipes or
320
+ // pathname output. The group is private to this command, so terminate it
321
+ // immediately instead of waiting for descendant-held streams to reach
322
+ // EOF and turning a completed leader into a timeout.
323
+ safelyKillPosixProcessGroup(child, "SIGKILL")
324
+ }
325
+ return exitCode
326
+ })
327
+ const stdoutTask = readBoundedText(
328
+ child.stdout,
329
+ maxStdoutBytes,
330
+ streamAbort.signal,
331
+ "VTracer emitted too much primary output.",
332
+ )
333
+ const stderrTask = readBoundedText(
334
+ child.stderr,
335
+ MAX_COMMAND_OUTPUT_BYTES,
336
+ streamAbort.signal,
337
+ "VTracer emitted too much diagnostic output.",
338
+ )
339
+ const primaryOutputTask =
340
+ primaryOutput === undefined
341
+ ? Promise.resolve<string | undefined>(undefined)
342
+ : readBoundedFifo(
343
+ primaryOutput.handle,
344
+ primaryOutput.maximumBytes,
345
+ streamAbort.signal,
346
+ () => childHasExited,
347
+ )
348
+ const executionTask = Promise.all([
349
+ childExitTask,
350
+ stdoutTask,
351
+ stderrTask,
352
+ primaryOutputTask,
353
+ ]).then(([exitCode, stdout, stderr, boundedPrimaryOutput]) => {
354
+ if (exitCode !== 0) {
355
+ throw new VectorizeError(
356
+ failureCode,
357
+ [
358
+ `Command failed (${exitCode}): ${command[0] ?? "unknown"}`,
359
+ stderr.trim(),
360
+ ]
361
+ .filter(Boolean)
362
+ .join("\n"),
363
+ { exitCode },
364
+ )
365
+ }
366
+ return boundedPrimaryOutput === undefined
367
+ ? { stderr, stdout }
368
+ : { primaryOutput: boundedPrimaryOutput, stderr, stdout }
369
+ })
370
+
371
+ let timer: ReturnType<typeof setTimeout> | undefined
372
+ const timeoutTask = new Promise<never>((_resolve, reject) => {
373
+ timer = setTimeout(() => reject(timeoutMarker), timeoutMs)
374
+ })
375
+
376
+ try {
377
+ return await Promise.race([executionTask, timeoutTask])
378
+ } catch (error) {
379
+ let cleanupError: unknown
380
+ try {
381
+ await terminateAndWait(child, ownsProcessGroup)
382
+ } catch (caught) {
383
+ cleanupError = caught
384
+ } finally {
385
+ streamAbort.abort()
386
+ await settlesWithin(
387
+ Promise.allSettled([stdoutTask, stderrTask, primaryOutputTask]),
388
+ HARD_KILL_WAIT_MS,
389
+ )
390
+ }
391
+
392
+ if (error === timeoutMarker) {
393
+ throw new VectorizeError(
394
+ "timeout",
395
+ cleanupError === undefined
396
+ ? "VTracer exceeded the conversion time limit."
397
+ : "VTracer exceeded the conversion time limit and did not terminate cleanly.",
398
+ cleanupError === undefined ? {} : { cleanup: String(cleanupError) },
399
+ )
400
+ }
401
+ if (error instanceof VectorizeError) throw error
402
+ if (cleanupError instanceof VectorizeError) throw cleanupError
403
+ throw executionError(failureCode, error)
404
+ } finally {
405
+ if (timer !== undefined) clearTimeout(timer)
406
+ activePosixProcessGroups.delete(child.pid)
407
+ }
408
+ }
409
+
410
+ function assertPositiveLimit(value: number, message: string): void {
411
+ if (!Number.isSafeInteger(value) || value < 1) {
412
+ throw new VectorizeError("invalid_input", message)
413
+ }
414
+ }
415
+
416
+ function remainingCommandTime(startedAt: number, timeoutMs: number): number {
417
+ return Math.floor(timeoutMs - (performance.now() - startedAt))
418
+ }
419
+
420
+ async function createPrivateFifo(
421
+ path: string,
422
+ timeoutMs: number,
423
+ failureCode: VectorizeErrorCode,
424
+ ): Promise<void> {
425
+ await runBoundedCommandInternal(
426
+ ["mkfifo", "-m", "600", path],
427
+ timeoutMs,
428
+ failureCode,
429
+ {},
430
+ )
431
+ }
432
+
433
+ async function assertOpenedPrivateDirectory(
434
+ path: string,
435
+ directory: FileHandle,
436
+ ): Promise<void> {
437
+ const [pathMetadata, openedMetadata] = await Promise.all([
438
+ lstat(path),
439
+ directory.stat(),
440
+ ])
441
+ if (
442
+ !pathMetadata.isDirectory() ||
443
+ !openedMetadata.isDirectory() ||
444
+ pathMetadata.dev !== openedMetadata.dev ||
445
+ pathMetadata.ino !== openedMetadata.ino
446
+ ) {
447
+ throw new VectorizeError(
448
+ "trace_failed",
449
+ "The bounded primary-output directory changed during setup.",
450
+ )
451
+ }
452
+ }
453
+
454
+ async function assertOpenedPrivateFifo(
455
+ path: string,
456
+ anchor: FileHandle,
457
+ reader: FileHandle,
458
+ ): Promise<void> {
459
+ const [pathMetadata, anchorMetadata, readerMetadata] = await Promise.all([
460
+ lstat(path),
461
+ anchor.stat(),
462
+ reader.stat(),
463
+ ])
464
+ if (
465
+ !pathMetadata.isFIFO() ||
466
+ !anchorMetadata.isFIFO() ||
467
+ !readerMetadata.isFIFO() ||
468
+ pathMetadata.dev !== anchorMetadata.dev ||
469
+ pathMetadata.ino !== anchorMetadata.ino ||
470
+ anchorMetadata.dev !== readerMetadata.dev ||
471
+ anchorMetadata.ino !== readerMetadata.ino
472
+ ) {
473
+ throw new VectorizeError(
474
+ "trace_failed",
475
+ "The bounded primary-output endpoint changed during setup.",
476
+ )
477
+ }
478
+ }
479
+
480
+ async function removePrimaryOutputEndpoint(
481
+ directoryPath: string | undefined,
482
+ path: string | undefined,
483
+ reader: FileHandle | undefined,
484
+ anchor: FileHandle | undefined,
485
+ directoryHandle: FileHandle | undefined,
486
+ ): Promise<unknown | undefined> {
487
+ let cleanupError: unknown
488
+ if (directoryPath !== undefined && directoryHandle === undefined) {
489
+ try {
490
+ // This branch is reachable only if opening the freshly-created empty
491
+ // directory failed. rmdir never follows a replacement symlink.
492
+ await rmdir(directoryPath)
493
+ } catch (error) {
494
+ cleanupError ??= error
495
+ }
496
+ }
497
+ if (directoryPath !== undefined && directoryHandle !== undefined) {
498
+ await directoryHandle.chmod(0o700).catch((error: unknown) => {
499
+ cleanupError ??= error
500
+ })
501
+ try {
502
+ const [pathDirectoryMetadata, openedDirectoryMetadata] = await Promise.all([
503
+ lstat(directoryPath),
504
+ directoryHandle.stat(),
505
+ ])
506
+ if (
507
+ !pathDirectoryMetadata.isDirectory() ||
508
+ !openedDirectoryMetadata.isDirectory() ||
509
+ pathDirectoryMetadata.dev !== openedDirectoryMetadata.dev ||
510
+ pathDirectoryMetadata.ino !== openedDirectoryMetadata.ino
511
+ ) {
512
+ throw new VectorizeError(
513
+ "trace_failed",
514
+ "The bounded primary-output directory changed before cleanup.",
515
+ )
516
+ }
517
+ if (path !== undefined && cleanupError === undefined) {
518
+ const pathMetadata = await lstat(path).catch((error: unknown) => {
519
+ if (isFileSystemError(error, "ENOENT")) return undefined
520
+ throw error
521
+ })
522
+ const openedMetadata = await (reader ?? anchor)?.stat()
523
+ if (pathMetadata === undefined && openedMetadata !== undefined) {
524
+ throw new VectorizeError(
525
+ "trace_failed",
526
+ "The bounded primary-output endpoint disappeared before cleanup.",
527
+ )
528
+ }
529
+ if (pathMetadata !== undefined) {
530
+ if (
531
+ !pathMetadata.isFIFO() ||
532
+ (openedMetadata !== undefined &&
533
+ (pathMetadata.dev !== openedMetadata.dev ||
534
+ pathMetadata.ino !== openedMetadata.ino))
535
+ ) {
536
+ throw new VectorizeError(
537
+ "trace_failed",
538
+ "The bounded primary-output endpoint changed before cleanup.",
539
+ )
540
+ }
541
+ await unlink(path)
542
+ }
543
+ }
544
+ if (cleanupError === undefined) await rmdir(directoryPath)
545
+ } catch (error) {
546
+ cleanupError ??= error
547
+ }
548
+ }
549
+ const closes = await Promise.allSettled([
550
+ reader?.close(),
551
+ anchor?.close(),
552
+ directoryHandle?.close(),
553
+ ])
554
+ cleanupError ??= closes.find(
555
+ (result): result is PromiseRejectedResult => result.status === "rejected",
556
+ )?.reason
557
+ return cleanupError
558
+ }
559
+
560
+ async function terminateAndWait(
561
+ child: ManagedSubprocess,
562
+ ownsProcessGroup: boolean,
563
+ ): Promise<void> {
564
+ if (process.platform === "win32") {
565
+ await killWindowsProcessTree(child.pid)
566
+ } else if (!ownsProcessGroup) {
567
+ safelyKillChild(child, "SIGTERM")
568
+ await delay(TERMINATION_GRACE_MS)
569
+ safelyKillChild(child, "SIGKILL")
570
+ } else {
571
+ safelyKillPosixProcessGroup(child, "SIGTERM")
572
+ await delay(TERMINATION_GRACE_MS)
573
+ // The group may still contain descendants after its leader exits.
574
+ safelyKillPosixProcessGroup(child, "SIGKILL")
575
+ }
576
+ if (await settlesWithin(child.exited, HARD_KILL_WAIT_MS)) return
577
+ throw new VectorizeError(
578
+ "trace_failed",
579
+ "VTracer did not exit after forced termination.",
580
+ )
581
+ }
582
+
583
+ function safelyKillChild(
584
+ child: ManagedSubprocess,
585
+ signal: NodeJS.Signals,
586
+ ): void {
587
+ try {
588
+ child.kill(signal)
589
+ } catch {
590
+ // A concurrent exit is equivalent to successful termination.
591
+ }
592
+ }
593
+
594
+ function safelyKillPosixProcessGroup(
595
+ child: ManagedSubprocess,
596
+ signal: NodeJS.Signals,
597
+ ): void {
598
+ if (!safelyKillPosixProcessGroupByPid(child.pid, signal)) {
599
+ try {
600
+ child.kill(signal)
601
+ } catch {
602
+ // A concurrent group exit is equivalent to successful termination.
603
+ }
604
+ }
605
+ }
606
+
607
+ function safelyKillPosixProcessGroupByPid(
608
+ pid: number,
609
+ signal: NodeJS.Signals,
610
+ ): boolean {
611
+ try {
612
+ process.kill(-pid, signal)
613
+ return true
614
+ } catch {
615
+ return false
616
+ }
617
+ }
618
+
619
+ async function killWindowsProcessTree(pid: number): Promise<void> {
620
+ let killer: ManagedSubprocess
621
+ try {
622
+ killer = Bun.spawn(
623
+ ["taskkill.exe", "/PID", String(pid), "/T", "/F"],
624
+ {
625
+ detached: false,
626
+ env: nonGatewayChildEnvironment(),
627
+ stderr: "pipe",
628
+ stdin: "ignore",
629
+ stdout: "pipe",
630
+ windowsHide: true,
631
+ },
632
+ )
633
+ } catch {
634
+ return
635
+ }
636
+ const drain = Promise.all([
637
+ readBoundedText(
638
+ killer.stdout,
639
+ MAX_COMMAND_OUTPUT_BYTES,
640
+ AbortSignal.timeout(HARD_KILL_WAIT_MS),
641
+ "Process-tree cleanup emitted too much output.",
642
+ ),
643
+ readBoundedText(
644
+ killer.stderr,
645
+ MAX_COMMAND_OUTPUT_BYTES,
646
+ AbortSignal.timeout(HARD_KILL_WAIT_MS),
647
+ "Process-tree cleanup emitted too much output.",
648
+ ),
649
+ ])
650
+ if (!(await settlesWithin(Promise.all([killer.exited, drain]), HARD_KILL_WAIT_MS))) {
651
+ try {
652
+ killer.kill("SIGKILL")
653
+ } catch {
654
+ // Cleanup is already best-effort after a taskkill timeout.
655
+ }
656
+ }
657
+ }
658
+
659
+ async function settlesWithin(promise: Promise<unknown>, durationMs: number): Promise<boolean> {
660
+ return new Promise((resolve) => {
661
+ let finished = false
662
+ const timer = setTimeout(() => finish(false), durationMs)
663
+ void promise.then(
664
+ () => finish(true),
665
+ () => finish(true),
666
+ )
667
+ function finish(settled: boolean): void {
668
+ if (finished) return
669
+ finished = true
670
+ clearTimeout(timer)
671
+ resolve(settled)
672
+ }
673
+ })
674
+ }
675
+
676
+ async function readBoundedText(
677
+ stream: ReadableStream<Uint8Array>,
678
+ maximumBytes: number,
679
+ signal: AbortSignal,
680
+ limitMessage: string,
681
+ ): Promise<string> {
682
+ const reader = stream.getReader()
683
+ const chunks: Uint8Array[] = []
684
+ let bytes = 0
685
+ const cancel = (): void => {
686
+ void reader.cancel().catch(() => undefined)
687
+ }
688
+ signal.addEventListener("abort", cancel, { once: true })
689
+ try {
690
+ while (true) {
691
+ const { done, value } = await reader.read()
692
+ if (done) break
693
+ bytes += value.byteLength
694
+ if (bytes > maximumBytes) {
695
+ await reader.cancel()
696
+ throw new VectorizeError("output_limit", limitMessage, {
697
+ bytes,
698
+ maximumBytes,
699
+ })
700
+ }
701
+ chunks.push(value)
702
+ }
703
+ return Buffer.concat(chunks, bytes).toString("utf8")
704
+ } finally {
705
+ signal.removeEventListener("abort", cancel)
706
+ reader.releaseLock()
707
+ }
708
+ }
709
+
710
+ async function readBoundedFifo(
711
+ handle: FileHandle,
712
+ maximumBytes: number,
713
+ signal: AbortSignal,
714
+ childHasExited: () => boolean,
715
+ ): Promise<string> {
716
+ const buffer = Buffer.allocUnsafe(
717
+ Math.min(PRIMARY_OUTPUT_READ_BYTES, maximumBytes + 1),
718
+ )
719
+ const chunks: Uint8Array[] = []
720
+ let bytes = 0
721
+ let emptyPollsAfterExit = 0
722
+ while (!signal.aborted) {
723
+ const maximumRead = Math.min(
724
+ buffer.byteLength,
725
+ maximumBytes - bytes + 1,
726
+ )
727
+ try {
728
+ const { bytesRead } = await handle.read(
729
+ buffer,
730
+ 0,
731
+ maximumRead,
732
+ null,
733
+ )
734
+ if (bytesRead > 0) {
735
+ emptyPollsAfterExit = 0
736
+ bytes += bytesRead
737
+ if (bytes > maximumBytes) {
738
+ throw new VectorizeError(
739
+ "output_limit",
740
+ "VTracer emitted too much primary output.",
741
+ { bytes, maximumBytes },
742
+ )
743
+ }
744
+ chunks.push(Uint8Array.from(buffer.subarray(0, bytesRead)))
745
+ continue
746
+ }
747
+ } catch (error) {
748
+ if (!isWouldBlockError(error)) throw error
749
+ }
750
+ if (childHasExited()) {
751
+ emptyPollsAfterExit += 1
752
+ // A fast child can write and exit after this nonblocking read reports
753
+ // empty but before its exit promise updates our state. Require another
754
+ // empty read after observing the exit so queued FIFO bytes are drained.
755
+ if (emptyPollsAfterExit >= PRIMARY_OUTPUT_EXIT_EMPTY_POLLS) break
756
+ } else {
757
+ emptyPollsAfterExit = 0
758
+ }
759
+ await delay(PRIMARY_OUTPUT_POLL_MS)
760
+ }
761
+ return Buffer.concat(chunks, bytes).toString("utf8")
762
+ }
763
+
764
+ function isWouldBlockError(error: unknown): boolean {
765
+ return (
766
+ isFileSystemError(error) &&
767
+ ["EAGAIN", "EINTR", "EWOULDBLOCK"].includes(
768
+ String(error.code),
769
+ )
770
+ )
771
+ }
772
+
773
+ function isFileSystemError(
774
+ error: unknown,
775
+ code?: string,
776
+ ): error is NodeJS.ErrnoException {
777
+ return (
778
+ error instanceof Error &&
779
+ "code" in error &&
780
+ (code === undefined || (error as NodeJS.ErrnoException).code === code)
781
+ )
782
+ }
783
+
784
+ function delay(durationMs: number): Promise<void> {
785
+ return new Promise((resolve) => {
786
+ setTimeout(resolve, durationMs)
787
+ })
788
+ }
789
+
790
+ function executionError(failureCode: VectorizeErrorCode, cause: unknown): VectorizeError {
791
+ return new VectorizeError(
792
+ failureCode,
793
+ "VTracer could not be executed.",
794
+ {},
795
+ { cause: cause instanceof Error ? cause : new Error(String(cause)) },
796
+ )
797
+ }