@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,323 @@
1
+ import { constants } from "node:fs"
2
+ import { open, realpath, type FileHandle } from "node:fs/promises"
3
+ import { resolve } from "node:path"
4
+ import sharp from "sharp"
5
+ import { type VectorizeDeadline } from "./limits.js"
6
+ import { sha256 } from "./metrics.js"
7
+ import { VectorizeError, type VectorizeInput, type VectorizeLimits } from "./types.js"
8
+
9
+ const allowedFormats = new Set(["avif", "gif", "heif", "jpeg", "png", "tiff", "webp"])
10
+ const METRIC_MAX_EDGE = 512
11
+ export const VECTORIZE_SHARP_CONCURRENCY = 1
12
+
13
+ export function configureVectorizeSharpConcurrency(): void {
14
+ // Each isolated vectorizer declares one host CPU unit. Bind libvips to the
15
+ // same budget inside that worker without mutating an SDK caller's Sharp pool.
16
+ const actual = sharp.concurrency(VECTORIZE_SHARP_CONCURRENCY)
17
+ if (actual !== VECTORIZE_SHARP_CONCURRENCY) {
18
+ throw new VectorizeError(
19
+ "trace_failed",
20
+ "The vectorization worker could not bind its Sharp CPU budget.",
21
+ )
22
+ }
23
+ }
24
+
25
+ export interface LoadedRaster {
26
+ readonly bytes: Uint8Array
27
+ readonly format: string
28
+ readonly height: number
29
+ readonly inputBytes: number
30
+ readonly pixels: Uint8Array
31
+ readonly scoreHeight: number
32
+ readonly scorePixels: Uint8Array
33
+ readonly scoreWidth: number
34
+ readonly sourceSha256: string
35
+ readonly width: number
36
+ }
37
+
38
+ export async function loadRaster(
39
+ input: VectorizeInput,
40
+ limits: Readonly<VectorizeLimits>,
41
+ deadline: VectorizeDeadline,
42
+ ): Promise<LoadedRaster> {
43
+ deadline.assert("input read")
44
+ const bytes = await readInputBytes(input, limits.maxInputBytes, deadline)
45
+ deadline.assert("input metadata")
46
+ try {
47
+ const metadata = await sharp(bytes, {
48
+ failOn: "error",
49
+ limitInputPixels: limits.maxDecodedPixels,
50
+ sequentialRead: true,
51
+ }).metadata()
52
+ const format = metadata.format
53
+ if (format === undefined || !allowedFormats.has(format)) {
54
+ throw new VectorizeError(
55
+ "invalid_input",
56
+ `Expected a supported raster image, received ${format ?? "an unknown format"}.`,
57
+ )
58
+ }
59
+ if ((metadata.pages ?? 1) !== 1) {
60
+ throw new VectorizeError("invalid_input", "Animated and multipage raster inputs are rejected.")
61
+ }
62
+ if (
63
+ metadata.width === undefined ||
64
+ metadata.height === undefined ||
65
+ metadata.width < 1 ||
66
+ metadata.height < 1
67
+ ) {
68
+ throw new VectorizeError("invalid_input", "Raster dimensions are missing or invalid.")
69
+ }
70
+ assertDimensions(metadata.width, metadata.height, limits)
71
+
72
+ const decoded = await sharp(bytes, {
73
+ failOn: "error",
74
+ limitInputPixels: limits.maxDecodedPixels,
75
+ sequentialRead: true,
76
+ })
77
+ .rotate()
78
+ .ensureAlpha()
79
+ .raw()
80
+ .toBuffer({ resolveWithObject: true })
81
+ const { channels, height, width } = decoded.info
82
+ if (channels !== 4) {
83
+ throw new VectorizeError("invalid_input", "Raster decoding did not produce RGBA pixels.")
84
+ }
85
+ assertDimensions(width, height, limits)
86
+ const pixels = Uint8Array.from(decoded.data)
87
+ if (!containsVisiblePixel(pixels)) {
88
+ throw new VectorizeError("invalid_input", "A fully transparent image cannot be vectorized.")
89
+ }
90
+ deadline.assert("raster decode")
91
+
92
+ const scoreScale = Math.min(1, METRIC_MAX_EDGE / Math.max(width, height))
93
+ const scoreWidth = Math.max(1, Math.round(width * scoreScale))
94
+ const scoreHeight = Math.max(1, Math.round(height * scoreScale))
95
+ const scorePixels =
96
+ scoreWidth === width && scoreHeight === height
97
+ ? pixels
98
+ : Uint8Array.from(
99
+ await sharp(pixels, { raw: { channels: 4, height, width } })
100
+ .resize(scoreWidth, scoreHeight, { fit: "fill", kernel: "lanczos3" })
101
+ .raw()
102
+ .toBuffer(),
103
+ )
104
+ deadline.assert("metric sample")
105
+ return {
106
+ bytes,
107
+ format,
108
+ height,
109
+ inputBytes: bytes.byteLength,
110
+ pixels,
111
+ scoreHeight,
112
+ scorePixels,
113
+ scoreWidth,
114
+ sourceSha256: sha256(bytes),
115
+ width,
116
+ }
117
+ } catch (error) {
118
+ if (error instanceof VectorizeError) throw error
119
+ throw new VectorizeError("invalid_input", "Raster input could not be decoded safely.", {}, {
120
+ cause: error,
121
+ })
122
+ }
123
+ }
124
+
125
+ async function readInputBytes(
126
+ input: VectorizeInput,
127
+ maximumBytes: number,
128
+ deadline: VectorizeDeadline,
129
+ ): Promise<Uint8Array> {
130
+ if (typeof input === "string") {
131
+ return readRegularInput(resolve(input), maximumBytes, deadline)
132
+ }
133
+ const view = input instanceof ArrayBuffer ? new Uint8Array(input) : input
134
+ if (view.byteLength === 0) {
135
+ throw new VectorizeError("invalid_input", "Raster input is empty.")
136
+ }
137
+ if (view.byteLength > maximumBytes) {
138
+ throw new VectorizeError(
139
+ "input_limit",
140
+ `Raster input exceeds the ${maximumBytes}-byte limit.`,
141
+ { bytes: view.byteLength, maximumBytes },
142
+ )
143
+ }
144
+ deadline.assert("input read")
145
+ const bytes = Uint8Array.from(view)
146
+ deadline.assert("input read")
147
+ return bytes
148
+ }
149
+
150
+ async function readRegularInput(
151
+ path: string,
152
+ maximumBytes: number,
153
+ deadline: VectorizeDeadline,
154
+ ): Promise<Uint8Array> {
155
+ let handle: FileHandle | undefined
156
+ try {
157
+ deadline.assert("input read")
158
+ const targetPath = await realpath(path)
159
+ deadline.assert("input read")
160
+ handle = await open(targetPath, boundedReadFlags())
161
+ const metadata = await handle.stat()
162
+ if (!metadata.isFile()) {
163
+ throw new VectorizeError("invalid_input", `Raster input is not a file: ${path}`)
164
+ }
165
+ if (metadata.size < 1) {
166
+ throw new VectorizeError("invalid_input", "Raster input is empty.")
167
+ }
168
+ if (metadata.size > maximumBytes) {
169
+ throw new VectorizeError(
170
+ "input_limit",
171
+ `Raster input exceeds the ${maximumBytes}-byte limit.`,
172
+ { bytes: metadata.size, maximumBytes },
173
+ )
174
+ }
175
+
176
+ const chunk = Buffer.allocUnsafe(Math.min(64 * 1_024, maximumBytes + 1))
177
+ const chunks: Buffer[] = []
178
+ let bytes = 0
179
+ while (true) {
180
+ deadline.assert("input read")
181
+ const maximumRead = Math.min(chunk.byteLength, maximumBytes - bytes + 1)
182
+ const { bytesRead } = await handle.read(chunk, 0, maximumRead, null)
183
+ if (bytesRead === 0) break
184
+ bytes += bytesRead
185
+ if (bytes > maximumBytes) {
186
+ throw new VectorizeError(
187
+ "input_limit",
188
+ `Raster input exceeds the ${maximumBytes}-byte limit.`,
189
+ { bytes, maximumBytes },
190
+ )
191
+ }
192
+ chunks.push(Buffer.from(chunk.subarray(0, bytesRead)))
193
+ }
194
+ if (bytes === 0) {
195
+ throw new VectorizeError("invalid_input", "Raster input is empty.")
196
+ }
197
+ deadline.assert("input read")
198
+ return Buffer.concat(chunks, bytes)
199
+ } catch (error) {
200
+ if (error instanceof VectorizeError) throw error
201
+ throw new VectorizeError(
202
+ "invalid_input",
203
+ "Raster input could not be read safely.",
204
+ {},
205
+ { cause: error },
206
+ )
207
+ } finally {
208
+ await handle?.close().catch(() => undefined)
209
+ }
210
+ }
211
+
212
+ function assertDimensions(
213
+ width: number,
214
+ height: number,
215
+ limits: Readonly<VectorizeLimits>,
216
+ ): void {
217
+ if (
218
+ !Number.isInteger(width) ||
219
+ !Number.isInteger(height) ||
220
+ width < 1 ||
221
+ height < 1 ||
222
+ width > limits.maxDimension ||
223
+ height > limits.maxDimension ||
224
+ width * height > limits.maxDecodedPixels
225
+ ) {
226
+ throw new VectorizeError(
227
+ "input_limit",
228
+ `Raster dimensions must fit ${limits.maxDimension}px and ${limits.maxDecodedPixels} decoded pixels.`,
229
+ { height, width },
230
+ )
231
+ }
232
+ }
233
+
234
+ function containsVisiblePixel(rgba: Uint8Array): boolean {
235
+ for (let index = 3; index < rgba.length; index += 4) {
236
+ if (rgba[index]! > 0) return true
237
+ }
238
+ return false
239
+ }
240
+
241
+ function boundedReadFlags(): number {
242
+ if (process.platform === "win32") return constants.O_RDONLY
243
+ return constants.O_RDONLY | constants.O_NONBLOCK | constants.O_NOFOLLOW
244
+ }
245
+
246
+ export async function encodeTracePng(
247
+ pixels: Uint8Array,
248
+ width: number,
249
+ height: number,
250
+ ): Promise<Uint8Array> {
251
+ return Uint8Array.from(
252
+ await sharp(pixels, { raw: { channels: 4, height, width } })
253
+ .png({ adaptiveFiltering: false, compressionLevel: 9, palette: false })
254
+ .toBuffer(),
255
+ )
256
+ }
257
+
258
+ export async function renderSvgRgba(
259
+ svg: string,
260
+ width: number,
261
+ height: number,
262
+ maxDecodedPixels: number,
263
+ ): Promise<Uint8Array> {
264
+ try {
265
+ return Uint8Array.from(
266
+ await sharp(Buffer.from(svg), {
267
+ density: 72,
268
+ failOn: "error",
269
+ limitInputPixels: maxDecodedPixels,
270
+ })
271
+ .resize(width, height, { fit: "fill" })
272
+ .ensureAlpha()
273
+ .raw()
274
+ .toBuffer(),
275
+ )
276
+ } catch (error) {
277
+ throw new VectorizeError(
278
+ "trace_failed",
279
+ "Canonical SVG could not be rendered for quality measurement.",
280
+ {},
281
+ { cause: error },
282
+ )
283
+ }
284
+ }
285
+
286
+ export function sharpProvenance(): Readonly<{
287
+ sharp: string
288
+ sharpVersions: Readonly<Record<string, string>>
289
+ vips: string
290
+ }> {
291
+ const sharpVersions = normalizedPixelToolchain(sharp.versions)
292
+ const sharpVersion = sharpVersions.sharp
293
+ const vipsVersion = sharpVersions.vips
294
+ if (sharpVersion === undefined || vipsVersion === undefined) {
295
+ throw new VectorizeError(
296
+ "tool_version",
297
+ "Sharp must report its own and libvips version metadata.",
298
+ )
299
+ }
300
+ return {
301
+ sharp: sharpVersion,
302
+ sharpVersions,
303
+ vips: vipsVersion,
304
+ }
305
+ }
306
+
307
+ export function normalizedPixelToolchain(
308
+ versions: Readonly<Record<string, string | undefined>>,
309
+ ): Readonly<Record<string, string>> {
310
+ const entries = Object.entries(versions)
311
+ .filter((entry): entry is [string, string] => entry[1] !== undefined)
312
+ .sort(([left], [right]) => left.localeCompare(right))
313
+ if (
314
+ entries.length === 0 ||
315
+ entries.some(([name, version]) => name.length === 0 || version.length === 0)
316
+ ) {
317
+ throw new VectorizeError(
318
+ "tool_version",
319
+ "Pixel toolchain versions must be nonempty strings.",
320
+ )
321
+ }
322
+ return Object.freeze(Object.fromEntries(entries))
323
+ }
@@ -0,0 +1,339 @@
1
+ import { fileURLToPath } from "node:url"
2
+ import { mkdtemp, rm } from "node:fs/promises"
3
+ import { tmpdir } from "node:os"
4
+ import { dirname, join } from "node:path"
5
+ import { runBoundedCommand } from "./command.js"
6
+ import { resolveVectorizeLimits } from "./limits.js"
7
+ import {
8
+ MAX_VECTORIZE_REQUEST_BYTES,
9
+ MAX_VECTORIZE_RESPONSE_BYTES,
10
+ VECTORIZE_WORKER_PROTOCOL,
11
+ type VectorizeWorkerRequest,
12
+ type VectorizeWorkerResponse,
13
+ } from "./worker-protocol.js"
14
+ import {
15
+ VectorizeError,
16
+ vectorizeProfileNames,
17
+ type VectorizeErrorCode,
18
+ type VectorizeInput,
19
+ type VectorizeOptions,
20
+ type VectorizeResult,
21
+ } from "./types.js"
22
+
23
+ const vectorizeErrorCodes = new Set<VectorizeErrorCode>([
24
+ "input_limit",
25
+ "invalid_input",
26
+ "output_limit",
27
+ "quality_limit",
28
+ "timeout",
29
+ "tool_download",
30
+ "tool_integrity",
31
+ "tool_platform",
32
+ "tool_version",
33
+ "trace_failed",
34
+ "unsafe_svg",
35
+ ])
36
+ const WORKER_SHUTDOWN_RESERVE_MS = 250
37
+ const WORKER_RESPONSE_RESERVE_MS = 100
38
+
39
+ export async function runVectorizeWorker(
40
+ input: VectorizeInput,
41
+ options: VectorizeOptions,
42
+ ): Promise<VectorizeResult> {
43
+ const startedAt = performance.now()
44
+ const limits = resolveVectorizeLimits(options.limits)
45
+ if (process.platform === "win32") {
46
+ throw new VectorizeError(
47
+ "tool_platform",
48
+ "Bounded VTracer streaming is unavailable on Windows.",
49
+ { platform: process.platform },
50
+ )
51
+ }
52
+ const workerInput = encodeInput(input, limits.maxInputBytes)
53
+ const temporaryRoot = await mkdtemp(join(tmpdir(), "slopcamera-vectorize-"))
54
+ let result: VectorizeResult
55
+ try {
56
+ result = await executeVectorizeWorker(
57
+ workerInput,
58
+ options,
59
+ limits,
60
+ startedAt,
61
+ temporaryRoot,
62
+ )
63
+ } finally {
64
+ await removeTemporaryRoot(temporaryRoot)
65
+ }
66
+ if (performance.now() - startedAt >= limits.maxDurationMs) {
67
+ throw new VectorizeError(
68
+ "timeout",
69
+ "Vectorization exceeded the conversion time limit during cleanup.",
70
+ )
71
+ }
72
+ return result
73
+ }
74
+
75
+ async function executeVectorizeWorker(
76
+ workerInput: VectorizeWorkerRequest["input"],
77
+ options: VectorizeOptions,
78
+ limits: ReturnType<typeof resolveVectorizeLimits>,
79
+ startedAt: number,
80
+ temporaryRoot: string,
81
+ ): Promise<VectorizeResult> {
82
+ const preparationRemainingMs =
83
+ limits.maxDurationMs - (performance.now() - startedAt)
84
+ if (
85
+ preparationRemainingMs <=
86
+ WORKER_SHUTDOWN_RESERVE_MS + WORKER_RESPONSE_RESERVE_MS
87
+ ) {
88
+ throw new VectorizeError(
89
+ "timeout",
90
+ "Vectorization has no remaining budget for isolated worker execution.",
91
+ )
92
+ }
93
+ const workerDurationMs = Math.floor(
94
+ preparationRemainingMs -
95
+ WORKER_SHUTDOWN_RESERVE_MS -
96
+ WORKER_RESPONSE_RESERVE_MS,
97
+ )
98
+ const inheritedFileDescriptors = inheritedDescriptors(
99
+ options.inheritedFileDescriptors,
100
+ )
101
+ const workerInheritedFileDescriptors = inheritedFileDescriptors.map(
102
+ (_descriptor, index) => index + 3,
103
+ )
104
+ const request: VectorizeWorkerRequest = {
105
+ input: workerInput,
106
+ options: cloneOptions(
107
+ options,
108
+ workerDurationMs,
109
+ workerInheritedFileDescriptors,
110
+ ),
111
+ protocol: VECTORIZE_WORKER_PROTOCOL,
112
+ temporaryRoot,
113
+ }
114
+ const requestBytes = Buffer.from(JSON.stringify(request))
115
+ if (requestBytes.byteLength > MAX_VECTORIZE_REQUEST_BYTES) {
116
+ throw new VectorizeError(
117
+ "input_limit",
118
+ "The vectorization worker request exceeds its IPC limit.",
119
+ {
120
+ bytes: requestBytes.byteLength,
121
+ maximumBytes: MAX_VECTORIZE_REQUEST_BYTES,
122
+ },
123
+ )
124
+ }
125
+ const remainingMs = limits.maxDurationMs - (performance.now() - startedAt)
126
+ if (remainingMs <= WORKER_SHUTDOWN_RESERVE_MS) {
127
+ throw new VectorizeError(
128
+ "timeout",
129
+ "Vectorization has no remaining budget for isolated worker startup and cleanup.",
130
+ )
131
+ }
132
+
133
+ const { stdout } = await runBoundedCommand(
134
+ [process.execPath, workerEntryPath()],
135
+ Math.floor(remainingMs - WORKER_SHUTDOWN_RESERVE_MS),
136
+ "trace_failed",
137
+ {
138
+ ...(inheritedFileDescriptors.length === 0
139
+ ? {}
140
+ : { inheritedFileDescriptors }),
141
+ maxStdoutBytes: MAX_VECTORIZE_RESPONSE_BYTES,
142
+ stdin: requestBytes,
143
+ },
144
+ )
145
+ if (performance.now() - startedAt >= limits.maxDurationMs) {
146
+ throw new VectorizeError(
147
+ "timeout",
148
+ "Vectorization exceeded the conversion time limit.",
149
+ )
150
+ }
151
+ const response = parseResponse(stdout)
152
+ if (!response.ok) {
153
+ throw new VectorizeError(
154
+ response.error.code,
155
+ response.error.message,
156
+ response.error.details,
157
+ )
158
+ }
159
+ assertResult(response.result, limits.maxOutputBytes)
160
+ if (performance.now() - startedAt >= limits.maxDurationMs) {
161
+ throw new VectorizeError(
162
+ "timeout",
163
+ "Vectorization exceeded the conversion time limit.",
164
+ )
165
+ }
166
+ return response.result
167
+ }
168
+
169
+ async function removeTemporaryRoot(temporaryRoot: string): Promise<void> {
170
+ try {
171
+ await rm(temporaryRoot, { force: true, recursive: true })
172
+ } catch (error) {
173
+ throw new VectorizeError(
174
+ "trace_failed",
175
+ "The isolated vectorization directory could not be removed.",
176
+ { temporaryRoot },
177
+ { cause: error },
178
+ )
179
+ }
180
+ }
181
+
182
+ function encodeInput(
183
+ input: VectorizeInput,
184
+ maximumInputBytes: number,
185
+ ): VectorizeWorkerRequest["input"] {
186
+ return typeof input === "string"
187
+ ? { kind: "path", value: input }
188
+ : encodeBytes(input, maximumInputBytes)
189
+ }
190
+
191
+ function encodeBytes(
192
+ input: Uint8Array | ArrayBuffer,
193
+ maximumInputBytes: number,
194
+ ): Readonly<{ kind: "bytes"; value: string }> {
195
+ const view = input instanceof ArrayBuffer ? new Uint8Array(input) : input
196
+ if (view.byteLength < 1) {
197
+ throw new VectorizeError("invalid_input", "Raster input is empty.")
198
+ }
199
+ if (view.byteLength > maximumInputBytes) {
200
+ throw new VectorizeError(
201
+ "input_limit",
202
+ `Raster input exceeds the ${maximumInputBytes}-byte limit.`,
203
+ { bytes: view.byteLength, maximumBytes: maximumInputBytes },
204
+ )
205
+ }
206
+ return {
207
+ kind: "bytes",
208
+ value: Buffer.from(view.buffer, view.byteOffset, view.byteLength).toString("base64"),
209
+ }
210
+ }
211
+
212
+ function cloneOptions(
213
+ options: VectorizeOptions,
214
+ workerDurationMs: number,
215
+ inheritedFileDescriptors: readonly number[],
216
+ ): VectorizeOptions {
217
+ return {
218
+ ...(options.alphaCutoff === undefined ? {} : { alphaCutoff: options.alphaCutoff }),
219
+ ...(options.cacheDirectory === undefined
220
+ ? {}
221
+ : { cacheDirectory: options.cacheDirectory }),
222
+ ...(options.duotone === undefined
223
+ ? {}
224
+ : { duotone: [options.duotone[0], options.duotone[1]] as const }),
225
+ ...(inheritedFileDescriptors.length === 0
226
+ ? {}
227
+ : { inheritedFileDescriptors }),
228
+ limits: {
229
+ ...options.limits,
230
+ maxDurationMs: workerDurationMs,
231
+ },
232
+ ...(options.outputPath === undefined ? {} : { outputPath: options.outputPath }),
233
+ }
234
+ }
235
+
236
+ function inheritedDescriptors(
237
+ value: readonly number[] | undefined,
238
+ ): readonly number[] {
239
+ const descriptors = value ?? []
240
+ if (
241
+ descriptors.length > 16
242
+ || descriptors.some((descriptor, index) => (
243
+ !Number.isSafeInteger(descriptor)
244
+ || descriptor < 0
245
+ || descriptor > 2_147_483_647
246
+ || descriptors.indexOf(descriptor) !== index
247
+ ))
248
+ ) {
249
+ throw new VectorizeError(
250
+ "invalid_input",
251
+ "Inherited vectorizer descriptors must be unique bounded integers.",
252
+ )
253
+ }
254
+ return Object.freeze([...descriptors])
255
+ }
256
+
257
+ function workerEntryPath(): string {
258
+ const modulePath = fileURLToPath(import.meta.url)
259
+ return modulePath.endsWith(".ts")
260
+ ? join(dirname(modulePath), "worker.ts")
261
+ : join(dirname(modulePath), "vectorize", "worker.js")
262
+ }
263
+
264
+ function parseResponse(stdout: string): VectorizeWorkerResponse {
265
+ let parsed: unknown
266
+ try {
267
+ parsed = JSON.parse(stdout)
268
+ } catch (error) {
269
+ throw new VectorizeError(
270
+ "trace_failed",
271
+ "The vectorization worker returned malformed output.",
272
+ {},
273
+ { cause: error },
274
+ )
275
+ }
276
+ if (!isRecord(parsed) || parsed.protocol !== VECTORIZE_WORKER_PROTOCOL) {
277
+ throw new VectorizeError(
278
+ "trace_failed",
279
+ "The vectorization worker returned an incompatible response.",
280
+ )
281
+ }
282
+ if (parsed.ok === true && isVectorizeResult(parsed.result)) {
283
+ return parsed as unknown as VectorizeWorkerResponse
284
+ }
285
+ if (
286
+ parsed.ok === false &&
287
+ isRecord(parsed.error) &&
288
+ typeof parsed.error.code === "string" &&
289
+ vectorizeErrorCodes.has(parsed.error.code as VectorizeErrorCode) &&
290
+ typeof parsed.error.message === "string" &&
291
+ isRecord(parsed.error.details)
292
+ ) {
293
+ return parsed as unknown as VectorizeWorkerResponse
294
+ }
295
+ throw new VectorizeError(
296
+ "trace_failed",
297
+ "The vectorization worker returned an invalid response.",
298
+ )
299
+ }
300
+
301
+ function assertResult(result: VectorizeResult, maximumOutputBytes: number): void {
302
+ const bytes = Buffer.byteLength(result.svg)
303
+ if (
304
+ bytes < 1 ||
305
+ bytes > maximumOutputBytes ||
306
+ result.receipt.bytes !== bytes ||
307
+ result.receipt.svgSha256.length !== 64
308
+ ) {
309
+ throw new VectorizeError(
310
+ "trace_failed",
311
+ "The vectorization worker response violates its output contract.",
312
+ { bytes, maximumOutputBytes },
313
+ )
314
+ }
315
+ }
316
+
317
+ function isVectorizeResult(value: unknown): value is VectorizeResult {
318
+ if (
319
+ !isRecord(value) ||
320
+ (value.outputPath !== null && typeof value.outputPath !== "string") ||
321
+ typeof value.svg !== "string" ||
322
+ !isRecord(value.receipt)
323
+ ) {
324
+ return false
325
+ }
326
+ const profile = value.receipt.profile
327
+ return (
328
+ typeof value.receipt.bytes === "number" &&
329
+ typeof value.receipt.svgSha256 === "string" &&
330
+ typeof profile === "string" &&
331
+ vectorizeProfileNames.includes(
332
+ profile as (typeof vectorizeProfileNames)[number],
333
+ )
334
+ )
335
+ }
336
+
337
+ function isRecord(value: unknown): value is Record<string, unknown> {
338
+ return typeof value === "object" && value !== null && !Array.isArray(value)
339
+ }