cozyclay 2.1.1 → 3.0.0

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 (577) hide show
  1. package/CHANGELOG.md +60 -0
  2. package/README.md +12 -30
  3. package/bin/agent/agent-routes.mjs +40 -113
  4. package/bin/agent/agent-runner.mjs +9 -10
  5. package/bin/agent/cliproxy-provider.mjs +135 -0
  6. package/bin/agent/credential-store.mjs +30 -7
  7. package/bin/agent/pi-tools.mjs +4 -1
  8. package/bin/agent/provider-keys.mjs +32 -0
  9. package/bin/agent/provider-login.mjs +110 -0
  10. package/bin/agent/provider-routes.mjs +99 -0
  11. package/bin/agent/providers.mjs +19 -97
  12. package/bin/agent/script-tool.mjs +17 -4
  13. package/bin/agent/studio-motion-reference.mjs +23 -0
  14. package/bin/agent/studio-prompt.mjs +1 -1
  15. package/bin/agent/studio-rebase-text.mjs +32 -0
  16. package/bin/agent/studio-rebase.mjs +26 -39
  17. package/bin/agent/studio-tools.mjs +9 -28
  18. package/bin/agent/tool-arguments.mjs +49 -0
  19. package/bin/agent/turn-frames.mjs +66 -0
  20. package/bin/codex-auth.mjs +9 -2
  21. package/bin/cozyclay.mjs +10 -116
  22. package/bin/live/cli.mjs +3 -3
  23. package/bin/mcp-runtime.mjs +2 -12
  24. package/bin/package-signature.mjs +0 -4
  25. package/dist/app/index.html +7 -6
  26. package/dist/assets/EXRLoader-CvVIvGVE.js +1 -0
  27. package/dist/assets/analytics-C9sUnndd.js +1 -0
  28. package/dist/assets/app-CCh--1Hc.js +5087 -0
  29. package/dist/assets/app-f-fkV5uP.css +1 -0
  30. package/dist/assets/compile-worker-CkxiD6lH.js +5 -0
  31. package/dist/assets/fflate.module-KbGj5-H6.js +5 -0
  32. package/dist/assets/{first-shot-handoff-msLI71g1.js → first-shot-handoff-CC4xe4HM.js} +1 -1
  33. package/dist/assets/{landing-ChUvB_zL.js → landing-DjHD8u7k.js} +1 -1
  34. package/dist/assets/scene-asset-sync-Hx_FzdOH.js +20 -0
  35. package/dist/assets/scene-asset-sync-_S8NQl5B.css +1 -0
  36. package/dist/assets/workflow-C4yKH1p4.js +179 -0
  37. package/dist/cozyclay-package.json +1 -1
  38. package/dist/index.html +3 -3
  39. package/dist/sw.js +3 -6
  40. package/dist/workflow/index.html +5 -4
  41. package/mcp/LIVE-PROTOCOL.md +7 -47
  42. package/mcp/README.md +16 -26
  43. package/mcp/live-hub.mjs +6 -10
  44. package/mcp/package.json +1 -3
  45. package/mcp/tool-handlers.mjs +4 -168
  46. package/package.json +3 -22
  47. package/src/App.jsx +150 -2624
  48. package/src/agent-progress.js +79 -0
  49. package/src/agent-turn-mark.js +30 -0
  50. package/src/analytics.js +1 -19
  51. package/src/app-context.js +5 -0
  52. package/src/app-stage.jsx +2 -34
  53. package/src/asset-pane.jsx +6 -5
  54. package/src/camera-block.js +21 -0
  55. package/src/camera-direction.js +120 -0
  56. package/src/character-kind.js +3 -9
  57. package/src/command-bus.js +8 -6
  58. package/src/commands/cast.js +5 -34
  59. package/src/commands/elements/character.js +1 -1
  60. package/src/commands/elements/motion.js +1 -6
  61. package/src/commands/elements/object.js +6 -2
  62. package/src/commands/elements/shot.js +4 -1
  63. package/src/commands/elements.js +38 -14
  64. package/src/commands/index.js +3 -1
  65. package/src/commands/kits.js +37 -0
  66. package/src/commands/motion-timing.js +57 -0
  67. package/src/commands/motion.js +9 -152
  68. package/src/commands/motion3-compile.js +65 -0
  69. package/src/commands/motion3.js +207 -0
  70. package/src/commands/objects.js +6 -4
  71. package/src/commands/shot.js +49 -11
  72. package/src/commands/stage.js +14 -1
  73. package/src/commands/view.js +4 -1
  74. package/src/document-mirrors.js +42 -0
  75. package/src/document-store.js +4 -0
  76. package/src/domains/cast.js +19 -194
  77. package/src/domains/motion/motion-range-pin-rules.js +1 -1
  78. package/src/domains/motion/take-source.js +51 -0
  79. package/src/domains/motion.js +60 -1838
  80. package/src/domains/scenes.js +12 -55
  81. package/src/domains/shots.js +2 -2
  82. package/src/domains/stage.js +4 -2
  83. package/src/draft-ghost.js +46 -0
  84. package/src/draft-ghost.jsx +129 -0
  85. package/src/dualview.jsx +4 -3
  86. package/src/effects/EffectsLayer.jsx +112 -0
  87. package/src/effects/effect-meshes.js +232 -0
  88. package/src/effects/effect-record.js +103 -0
  89. package/src/effects/emitter.js +59 -0
  90. package/src/effects/simulate.js +261 -0
  91. package/src/effects/state.js +59 -0
  92. package/src/execution-telemetry.js +2 -3
  93. package/src/first-success-guide.jsx +5 -5
  94. package/src/ghost-set.js +257 -0
  95. package/src/held-object-edit.jsx +113 -0
  96. package/src/held-object-pose.js +175 -0
  97. package/src/hierarchy-model.js +1 -1
  98. package/src/hierarchy-panel.jsx +1 -1
  99. package/src/imported-mesh.jsx +103 -0
  100. package/src/kits/held.js +84 -0
  101. package/src/kits/index.js +70 -0
  102. package/src/kits/smoking.js +57 -0
  103. package/src/light-blockers.js +93 -0
  104. package/src/motion/beat-board-model.js +84 -0
  105. package/src/motion/beat-board.css +124 -0
  106. package/src/motion/beat-board.jsx +59 -0
  107. package/src/motion/center-of-mass.js +66 -0
  108. package/src/motion/fix-collisions.js +850 -0
  109. package/src/motion/physics-review.js +118 -0
  110. package/src/{ardy → motion}/physics-support.js +1 -76
  111. package/src/motion/rig-snapshot.js +22 -0
  112. package/src/motion/take-segments.jsx +140 -0
  113. package/src/{ardy → motion}/timeline-coordinates.js +0 -21
  114. package/src/{ardy → motion}/timeline.css +1 -2
  115. package/src/{ardy → motion}/timeline.jsx +42 -413
  116. package/src/motion/waypoints.js +76 -0
  117. package/src/motion3/beats.js +83 -0
  118. package/src/motion3/body.js +158 -0
  119. package/src/motion3/bones.js +47 -0
  120. package/src/motion3/carried.js +47 -0
  121. package/src/motion3/compile-job.js +75 -0
  122. package/src/motion3/compile-worker.js +10 -0
  123. package/src/motion3/compiler/anchors.js +105 -0
  124. package/src/motion3/compiler/arm-orient.js +128 -0
  125. package/src/motion3/compiler/channels.js +125 -0
  126. package/src/motion3/compiler/compile.js +205 -0
  127. package/src/motion3/compiler/gait-rhythm.js +97 -0
  128. package/src/motion3/compiler/grip.js +171 -0
  129. package/src/motion3/compiler/head-steady.js +48 -0
  130. package/src/motion3/compiler/hold.js +134 -0
  131. package/src/motion3/compiler/idle.js +113 -0
  132. package/src/motion3/compiler/limbs.js +170 -0
  133. package/src/motion3/compiler/math.js +72 -0
  134. package/src/motion3/compiler/pose.js +162 -0
  135. package/src/motion3/compiler/quat.js +49 -0
  136. package/src/motion3/compiler/skeleton.js +74 -0
  137. package/src/motion3/compiler/validate-common.js +32 -0
  138. package/src/motion3/compiler/validate-goals.js +118 -0
  139. package/src/motion3/compiler/validate-objects.js +143 -0
  140. package/src/motion3/compiler/validate-parts.js +116 -0
  141. package/src/motion3/compiler/validate.js +122 -0
  142. package/src/motion3/compiler/vocab.js +73 -0
  143. package/src/motion3/compiler/walk-pelvis.js +275 -0
  144. package/src/motion3/compiler/walk.js +207 -0
  145. package/src/motion3/compiler/wrist-clamp.js +32 -0
  146. package/src/motion3/delta.js +47 -0
  147. package/src/motion3/describe.js +174 -0
  148. package/src/motion3/docs.js +27 -0
  149. package/src/motion3/edit-carry.js +21 -0
  150. package/src/motion3/fix/ease.js +16 -0
  151. package/src/motion3/fix/ground.js +37 -0
  152. package/src/motion3/fix/index.js +52 -0
  153. package/src/motion3/fix/push.js +71 -0
  154. package/src/motion3/fix/reach.js +46 -0
  155. package/src/motion3/fix/retime.js +79 -0
  156. package/src/motion3/fix/self.js +211 -0
  157. package/src/motion3/fix/slide.js +70 -0
  158. package/src/motion3/fix/turn.js +51 -0
  159. package/src/motion3/follow.js +39 -0
  160. package/src/motion3/free-motion.js +173 -0
  161. package/src/motion3/grip-choice.js +130 -0
  162. package/src/motion3/hold-plan.js +157 -0
  163. package/src/motion3/json-patch.js +77 -0
  164. package/src/motion3/key-aim.js +105 -0
  165. package/src/motion3/key-edit.js +82 -0
  166. package/src/motion3/lenient.js +127 -0
  167. package/src/motion3/object-keys.js +236 -0
  168. package/src/motion3/object-pose.js +182 -0
  169. package/src/motion3/object-tracks.js +12 -0
  170. package/src/motion3/pipeline.js +102 -0
  171. package/src/motion3/playback.js +51 -0
  172. package/src/motion3/probes.js +91 -0
  173. package/src/motion3/qa/body-checks.js +34 -0
  174. package/src/motion3/qa/check-names.js +14 -0
  175. package/src/motion3/qa/checks.js +121 -0
  176. package/src/motion3/qa/contact-checks.js +51 -0
  177. package/src/motion3/qa/explain.js +80 -0
  178. package/src/motion3/qa/fmt.js +5 -0
  179. package/src/motion3/qa/foot-checks.js +72 -0
  180. package/src/motion3/qa/geometry.js +77 -0
  181. package/src/motion3/qa/goals.js +191 -0
  182. package/src/motion3/qa/ground.js +60 -0
  183. package/src/motion3/qa/hints-contact.js +179 -0
  184. package/src/motion3/qa/hints-goal.js +160 -0
  185. package/src/motion3/qa/hints-knee.js +24 -0
  186. package/src/motion3/qa/hints-motion.js +209 -0
  187. package/src/motion3/qa/hints-object.js +91 -0
  188. package/src/motion3/qa/index.js +42 -0
  189. package/src/motion3/qa/key-checks.js +82 -0
  190. package/src/motion3/qa/near.js +96 -0
  191. package/src/motion3/qa/object-checks.js +135 -0
  192. package/src/motion3/qa/patch.js +161 -0
  193. package/src/motion3/qa/severity.js +38 -0
  194. package/src/motion3/qa/spec-checks.js +65 -0
  195. package/src/motion3/qa/style.js +218 -0
  196. package/src/motion3/qa/wrist-checks.js +69 -0
  197. package/src/motion3/qa/wrist-obs.js +60 -0
  198. package/src/motion3/refs.js +120 -0
  199. package/src/motion3/repair-tune.js +121 -0
  200. package/src/motion3/repair.js +83 -0
  201. package/src/motion3/report.js +67 -0
  202. package/src/motion3/rigs/index.js +14 -0
  203. package/src/motion3/rigs/x-bot-tpose-body.json +1 -0
  204. package/src/motion3/rigs/x-bot-tpose.json +1 -0
  205. package/src/motion3/rigs/y-bot-tpose-body.json +1 -0
  206. package/src/motion3/rigs/y-bot-tpose.json +1 -0
  207. package/src/motion3/scene.js +60 -0
  208. package/src/motion3/schema/brief.js +89 -0
  209. package/src/motion3/schema/fields.js +44 -0
  210. package/src/motion3/schema/spec-examples.js +46 -0
  211. package/src/motion3/schema/topics.js +111 -0
  212. package/src/motion3/schema/types.js +147 -0
  213. package/src/motion3/search.js +117 -0
  214. package/src/motion3/second-hand.js +112 -0
  215. package/src/motion3/skills/basic.js +82 -0
  216. package/src/motion3/skills/book.js +76 -0
  217. package/src/motion3/skills/car.js +145 -0
  218. package/src/motion3/skills/frames-auto.js +29 -0
  219. package/src/motion3/skills/index.js +83 -0
  220. package/src/motion3/skills/info.js +65 -0
  221. package/src/motion3/skills/smoke-kit.js +109 -0
  222. package/src/motion3/skills/smoke.js +160 -0
  223. package/src/motion3/spec-store.js +59 -0
  224. package/src/motion3/take-edit.js +155 -0
  225. package/src/motion3/take.js +63 -0
  226. package/src/motion3/timeline.js +127 -0
  227. package/src/motion3/views.js +121 -0
  228. package/src/motion3/walk-plan.js +119 -0
  229. package/src/object-catalog.jsx +7 -1
  230. package/src/object-colors.js +98 -0
  231. package/src/object-gizmo-parts.js +115 -0
  232. package/src/object-gizmo.jsx +37 -122
  233. package/src/object-glow.js +139 -0
  234. package/src/object-hidden.js +33 -0
  235. package/src/object-look.js +196 -0
  236. package/src/object-size.js +10 -0
  237. package/src/object-travel.js +71 -6
  238. package/src/panels/CameraPanel.jsx +7 -7
  239. package/src/panels/HdrEnvironmentControls.jsx +59 -0
  240. package/src/panels/LightPanel.jsx +25 -4
  241. package/src/panels/ObjectLookPanel.jsx +88 -0
  242. package/src/panels/PosePanel.jsx +2 -17
  243. package/src/panels/RigControlPanel.jsx +5 -53
  244. package/src/panels/SubjectBox.jsx +4 -1
  245. package/src/panels/details.css +14 -0
  246. package/src/posestudio.jsx +4 -27
  247. package/src/project-browser.jsx +3 -4
  248. package/src/project-resources.js +18 -31
  249. package/src/project.js +13 -104
  250. package/src/props.jsx +25 -280
  251. package/src/rail-dot-drag.js +23 -0
  252. package/src/render-passes.js +10 -0
  253. package/src/resource-status.jsx +1 -0
  254. package/src/result-modal.jsx +4 -19
  255. package/src/room.jsx +20 -12
  256. package/src/scene-assets.js +8 -6
  257. package/src/scene-light-place.js +72 -0
  258. package/src/scene-lights.jsx +148 -0
  259. package/src/scene-objects.js +31 -139
  260. package/src/scenes.js +15 -105
  261. package/src/semantic-edit.js +2 -5
  262. package/src/set-pieces.jsx +183 -0
  263. package/src/settings-menu.jsx +3 -11
  264. package/src/shell/BottomDock.jsx +9 -51
  265. package/src/shell/DetailsSlot.jsx +16 -200
  266. package/src/shell/GhostsMenuGroup.jsx +74 -0
  267. package/src/shell/LibrarySlot.jsx +1 -3
  268. package/src/shell/MenuBar.jsx +3 -3
  269. package/src/shell/PreferencesDialog.jsx +15 -264
  270. package/src/shell/PreferencesSlot.jsx +2 -8
  271. package/src/shell/TopBar.jsx +5 -84
  272. package/src/shell/ViewportToolbar.jsx +10 -70
  273. package/src/shell/agent-glass.css +19 -6
  274. package/src/shell/details-selection.js +42 -0
  275. package/src/shell/ghosts-menu.css +22 -0
  276. package/src/shell/glass-regions.css +29 -3
  277. package/src/shell/preferences.css +6 -1
  278. package/src/shot-authoring.js +4 -7
  279. package/src/soft-shadows.js +37 -0
  280. package/src/stage-attach-frames.js +12 -12
  281. package/src/stage-canvas-rigs.jsx +3 -3
  282. package/src/stage-capture.jsx +42 -8
  283. package/src/stage-characters.jsx +22 -216
  284. package/src/stage-editor-gizmos.jsx +6 -6
  285. package/src/stage-environment.js +93 -0
  286. package/src/stage-hdr-builtins.js +107 -0
  287. package/src/stage-hdr-image.js +116 -0
  288. package/src/stage-hdr-library.js +70 -0
  289. package/src/stage-hdr-map.js +94 -0
  290. package/src/stage-hdr.js +130 -0
  291. package/src/stage-lighting.js +149 -0
  292. package/src/stage-occlusion.js +210 -0
  293. package/src/stage-sky.jsx +159 -0
  294. package/src/stage-startup-defaults.js +5 -9
  295. package/src/studio-actions.js +12 -14
  296. package/src/studio-agent-commands.js +19 -16
  297. package/src/studio-agent-motion.js +9 -9
  298. package/src/studio-agent-protocol.js +41 -71
  299. package/src/studio-app-binding.js +25 -45
  300. package/src/studio-asset-list.js +17 -0
  301. package/src/studio-contact-sheet.js +37 -5
  302. package/src/studio-elements.js +27 -7
  303. package/src/studio-entity-bounds.js +18 -0
  304. package/src/studio-error-codes.js +8 -0
  305. package/src/studio-export-framing.js +152 -0
  306. package/src/studio-lighting-check.js +103 -0
  307. package/src/studio-motion-visual.js +150 -0
  308. package/src/studio-schema-errors.js +80 -0
  309. package/src/studio-support-gap.js +74 -0
  310. package/src/styles.css +1 -19
  311. package/src/timeline-defaults.js +5 -0
  312. package/src/timeline-extent.js +8 -20
  313. package/src/tutorial-walk.js +17 -0
  314. package/src/ui-scrub.js +1 -1
  315. package/src/use-shot-camera-tracks.js +12 -0
  316. package/src/view-ghosts.js +97 -0
  317. package/src/view-ghosts.jsx +49 -0
  318. package/src/workflow/AgentPanel.jsx +47 -166
  319. package/src/workflow/AgentProgressCard.jsx +107 -0
  320. package/src/workflow/AgentReplyText.jsx +37 -0
  321. package/src/workflow/AgentTurnSteps.jsx +84 -0
  322. package/src/workflow/LoginPanel.jsx +140 -0
  323. package/src/workflow/WorkflowBuilder.jsx +7 -23
  324. package/src/workflow/agent-client.js +54 -51
  325. package/src/workflow/agent-panel.css +71 -1
  326. package/src/workflow/agent-progress-card.css +167 -0
  327. package/src/workflow/agent-progress-view.js +47 -0
  328. package/src/workflow/agent-turn-steps.css +113 -0
  329. package/src/workflow/agent-turn.js +191 -0
  330. package/src/workflow/login-client.js +34 -0
  331. package/src/workflow/motion-input.js +18 -31
  332. package/src/workflow/node-schema.js +1 -1
  333. package/src/workflow/reply-markdown.js +69 -0
  334. package/src/workflow/scene-asset-sync.js +8 -8
  335. package/tools/agent-session-stats.mjs +61 -0
  336. package/tools/dev-full.mjs +5 -56
  337. package/tools/eslint-baseline.json +12 -13
  338. package/tools/knip-baseline.json +0 -179
  339. package/tools/loc-baseline.json +31 -81
  340. package/tools/motion3/extract-body.mjs +103 -0
  341. package/tools/motion3/extract-rig.mjs +85 -0
  342. package/tools/process-supervisor.mjs +0 -88
  343. package/tools/run-tests.mjs +8 -124
  344. package/bin/agent/motion-runtime.mjs +0 -76
  345. package/dist/ardy/cskel27-rest.json +0 -557
  346. package/dist/assets/analytics-C9pMpW7s.js +0 -1
  347. package/dist/assets/app-B_2XB9rw.css +0 -1
  348. package/dist/assets/app-DAuvKPP8.js +0 -4920
  349. package/dist/assets/config-Bx7pYsD9.js +0 -1
  350. package/dist/assets/config-DeoFTYzb.css +0 -1
  351. package/dist/assets/demo-CvKuBgy5.js +0 -1
  352. package/dist/assets/scene-asset-sync-DKkpjKBV.js +0 -19
  353. package/dist/assets/scene-asset-sync-DjJ_cJ2R.css +0 -1
  354. package/dist/assets/ticket-BPk2Ni95.js +0 -1
  355. package/dist/assets/workflow-jJRBR94Z.js +0 -179
  356. package/dist/demo/walk-then-stop.npz +0 -0
  357. package/mcp/ardy-prompts.mjs +0 -218
  358. package/src/ardy/auto-fix-panel.css +0 -108
  359. package/src/ardy/auto-physics.js +0 -1194
  360. package/src/ardy/client.js +0 -153
  361. package/src/ardy/convert.js +0 -313
  362. package/src/ardy/cskel27-neutral.js +0 -39
  363. package/src/ardy/cskel27.js +0 -77
  364. package/src/ardy/export.js +0 -165
  365. package/src/ardy/fix-collisions.js +0 -2257
  366. package/src/ardy/foot-lock.js +0 -230
  367. package/src/ardy/motion-calibration.js +0 -118
  368. package/src/ardy/motion-edit.js +0 -213
  369. package/src/ardy/motion-url.js +0 -55
  370. package/src/ardy/mp4-duration.js +0 -102
  371. package/src/ardy/npz.js +0 -595
  372. package/src/ardy/physics-panel.css +0 -21
  373. package/src/ardy/physics-panel.jsx +0 -89
  374. package/src/ardy/physics-refine.js +0 -111
  375. package/src/ardy/physics-review.js +0 -567
  376. package/src/ardy/physics-temporal.js +0 -54
  377. package/src/ardy/platform-fit-panel.css +0 -64
  378. package/src/ardy/platform-fit-panel.jsx +0 -41
  379. package/src/ardy/platform-fit.js +0 -326
  380. package/src/ardy/playback.js +0 -711
  381. package/src/ardy/pose-pin.js +0 -97
  382. package/src/ardy/prompt-clips.js +0 -30
  383. package/src/ardy/retime.js +0 -211
  384. package/src/ardy/root-drop.js +0 -369
  385. package/src/ardy/timeline-resize.js +0 -13
  386. package/src/ardy/to-cskel27.js +0 -225
  387. package/src/ardy/trim.js +0 -33
  388. package/src/ardy/waypoints.js +0 -369
  389. package/src/ardy-bridge-contract.js +0 -281
  390. package/src/domains/motion/motion-video-capture.js +0 -664
  391. package/src/i2v-motion-client.js +0 -77
  392. package/src/i2v-motion-studio.jsx +0 -180
  393. package/src/line-edit.js +0 -1348
  394. package/src/motion/generation.js +0 -105
  395. package/src/motion-readiness-ui.jsx +0 -67
  396. package/src/motion-readiness.js +0 -40
  397. package/src/motion-resources.js +0 -177
  398. package/src/motion-store.js +0 -65
  399. package/src/motion-trail.js +0 -601
  400. package/src/multimodel-ingest.js +0 -394
  401. package/src/panels/PromptBlocksPanel.jsx +0 -444
  402. package/src/panels/VideoCapturePanel.jsx +0 -181
  403. package/src/pose-extract/browser.js +0 -42
  404. package/src/pose-extract/detector.js +0 -100
  405. package/src/pose-extract/fit.js +0 -227
  406. package/src/pose-extract/image-frame.js +0 -51
  407. package/src/pose-extract/index.js +0 -58
  408. package/src/pose-extract/keyframes.js +0 -74
  409. package/src/pose-extract/landmarks.js +0 -162
  410. package/src/pose-extract/mirror.js +0 -153
  411. package/src/pose-extract/take.js +0 -131
  412. package/src/pose-extract/video-frames.js +0 -91
  413. package/src/take-recipe.js +0 -165
  414. package/src/trail-key-conflicts.js +0 -39
  415. package/src/trail-pick.js +0 -59
  416. package/src/use-i2v-capture-modal.js +0 -15
  417. package/tools/ardy/BRIDGE.md +0 -37
  418. package/tools/ardy/GVHMR-NPZ.md +0 -68
  419. package/tools/ardy/README.md +0 -124
  420. package/tools/ardy/__pycache__/cclay_gvhmr_worker.cpython-313.pyc +0 -0
  421. package/tools/ardy/artifacts.mjs +0 -34
  422. package/tools/ardy/bridge.mjs +0 -1732
  423. package/tools/ardy/bvh-cskel27.mjs +0 -1265
  424. package/tools/ardy/cclay_constrained_generate.py +0 -561
  425. package/tools/ardy/cclay_gvhmr_worker.py +0 -264
  426. package/tools/ardy/cclay_motion_edit.py +0 -510
  427. package/tools/ardy/cclay_sequence_generate.py +0 -705
  428. package/tools/ardy/cclay_worker.py +0 -304
  429. package/tools/ardy/dump-npz.py +0 -205
  430. package/tools/ardy/extract-rest.mjs +0 -299
  431. package/tools/ardy/extract.mjs +0 -507
  432. package/tools/ardy/footage.mjs +0 -467
  433. package/tools/ardy/gvhmr-floor.mjs +0 -102
  434. package/tools/ardy/gvhmr-to-cskel27.mjs +0 -21
  435. package/tools/ardy/gvhmr_fastpath.py +0 -168
  436. package/tools/ardy/gvhmr_trajectory.py +0 -240
  437. package/tools/ardy/merge-text-encoder.py +0 -160
  438. package/tools/ardy/mocap-metrics.mjs +0 -200
  439. package/tools/ardy/mocap-quality-gate.mjs +0 -110
  440. package/tools/ardy/motion-stabilize.mjs +0 -348
  441. package/tools/ardy/npz.mjs +0 -414
  442. package/tools/ardy/pose-to-npz.mjs +0 -106
  443. package/tools/ardy/prompt-limits.mjs +0 -5
  444. package/tools/ardy/run-edit-on-box.sh +0 -73
  445. package/tools/ardy/run-local.mjs +0 -233
  446. package/tools/ardy/run-on-box.sh +0 -593
  447. package/tools/ardy/run-sequence-on-box.sh +0 -177
  448. package/tools/ardy/runners/gvhmr-worker.mjs +0 -206
  449. package/tools/ardy/runners/index.mjs +0 -18
  450. package/tools/ardy/runners/local.mjs +0 -455
  451. package/tools/ardy/runners/proc.mjs +0 -125
  452. package/tools/ardy/runners/remote.mjs +0 -252
  453. package/tools/ardy/setup-local.mjs +0 -189
  454. package/tools/ardy/setup-text-encoder-on-box.sh +0 -93
  455. package/tools/ardy/setup-text-encoder.py +0 -306
  456. package/tools/ardy/smpl-cskel27.mjs +0 -178
  457. package/tools/ardy/visual-qa.mjs +0 -196
  458. package/tools/ardy/vq-car.mjs +0 -52
  459. package/tools/bench/EXP3.md +0 -61
  460. package/tools/bench/cclay_bench_extract_incam.py +0 -125
  461. package/tools/bench/cclay_bench_extract_obs.py +0 -204
  462. package/tools/bench/cclay_bench_runner.py +0 -42
  463. package/tools/bench/cube-contact.mjs +0 -162
  464. package/tools/bench/exp3.mjs +0 -152
  465. package/tools/bench/extract-bench-lib.mjs +0 -216
  466. package/tools/bench/extract-bench.mjs +0 -302
  467. package/tools/bench/fal-generate.mjs +0 -88
  468. package/tools/bench/fit/README.md +0 -189
  469. package/tools/bench/fit/camera.mjs +0 -59
  470. package/tools/bench/fit/contact.mjs +0 -419
  471. package/tools/bench/fit/footlock.mjs +0 -234
  472. package/tools/bench/fit/motion.mjs +0 -85
  473. package/tools/bench/fit/pin.mjs +0 -27
  474. package/tools/bench/fit/remote.mjs +0 -105
  475. package/tools/bench/fit-bench.mjs +0 -126
  476. package/tools/bench/fit-sanity.mjs +0 -45
  477. package/tools/bench/metrics.mjs +0 -313
  478. package/tools/bench/obs/depth.mjs +0 -258
  479. package/tools/bench/obs/extrinsics.mjs +0 -175
  480. package/tools/bench/obs/fit_mannequin_betas.py +0 -340
  481. package/tools/bench/obs/ground.mjs +0 -294
  482. package/tools/bench/obs/heading.mjs +0 -151
  483. package/tools/bench/obs/ladder.mjs +0 -555
  484. package/tools/bench/obs/mannequin-betas.json +0 -124
  485. package/tools/bench/obs/remote.mjs +0 -83
  486. package/tools/bench/obs/rest_joints.py +0 -52
  487. package/tools/bench/obs/ybot-targets.mjs +0 -49
  488. package/tools/bench/obs-bench.mjs +0 -520
  489. package/tools/bench/score.mjs +0 -343
  490. package/tools/bench/summarize.mjs +0 -86
  491. package/tools/demo-worker/README.md +0 -137
  492. package/tools/demo-worker/api-client.mjs +0 -233
  493. package/tools/demo-worker/generate.mjs +0 -301
  494. package/tools/demo-worker/index.mjs +0 -466
  495. package/tools/demo-worker/sign-core.mjs +0 -112
  496. package/tools/demo-worker/sign.mjs +0 -161
  497. package/tools/gt-render/browser.mjs +0 -182
  498. package/tools/gt-render/camera-math.mjs +0 -276
  499. package/tools/gt-render/page.mjs +0 -267
  500. package/tools/gt-render/render.mjs +0 -514
  501. package/tools/gt-render/scene-box.mjs +0 -31
  502. package/tools/gt-render/take-transform.mjs +0 -87
  503. package/tools/kimodo/compare-seam.mjs +0 -108
  504. package/tools/kimodo/constraints.mjs +0 -244
  505. package/tools/kimodo/edit.mjs +0 -259
  506. package/tools/kimodo/effector-constraints.mjs +0 -200
  507. package/tools/kimodo/generate.mjs +0 -646
  508. package/tools/kimodo/local-output.mjs +0 -265
  509. package/tools/kimodo/measure-pose.mjs +0 -65
  510. package/tools/kimodo/measure-preserve.mjs +0 -899
  511. package/tools/kimodo/measure-waypoints.mjs +0 -55
  512. package/tools/kimodo/mlx-generate.py +0 -111
  513. package/tools/kimodo/pose-constraints.mjs +0 -248
  514. package/tools/kimodo/preserve-mask.mjs +0 -543
  515. package/tools/kimodo/read-npz.mjs +0 -189
  516. package/tools/kimodo/run-edit-on-box.mjs +0 -155
  517. package/tools/kimodo/run-sequence-on-box.mjs +0 -178
  518. package/tools/kimodo/runner.mjs +0 -284
  519. package/tools/kimodo/seam-batch.mjs +0 -77
  520. package/tools/kimodo/setup-local.mjs +0 -30
  521. package/tools/kimodo/setup-on-box.sh +0 -339
  522. package/tools/kimodo/soma77-to-cskel27.mjs +0 -305
  523. package/tools/morphgs/assets/gen_truth.py +0 -39
  524. package/tools/morphgs/assets/mesh_ori_rig.txt +0 -27
  525. package/tools/morphgs/assets/playback-check.mjs +0 -30
  526. package/tools/morphgs/demo-gate.sh +0 -26
  527. package/tools/morphgs/fbx2morphgs.mjs +0 -68
  528. package/tools/morphgs/morphgs-to-cskel27.mjs +0 -105
  529. package/tools/morphgs/patches/preprocess_src-none-mode.patch +0 -76
  530. package/tools/morphgs/setup-on-cluster.sh +0 -126
  531. package/tools/projflow/driver.py +0 -1069
  532. package/tools/projflow/generate.mjs +0 -805
  533. package/tools/projflow/hml22-to-cskel27.mjs +0 -735
  534. package/tools/projflow/line-edit-job.mjs +0 -547
  535. package/tools/projflow/replay.mjs +0 -452
  536. package/tools/projflow/runner.mjs +0 -269
  537. package/tools/projflow/service.mjs +0 -742
  538. package/tools/track/DESIGN.md +0 -513
  539. package/tools/track/backfill-provenance.mjs +0 -116
  540. package/tools/track/budget.mjs +0 -8
  541. package/tools/track/check-rig.mjs +0 -209
  542. package/tools/track/diagnostics.schema.json +0 -60
  543. package/tools/track/export-rig.mjs +0 -256
  544. package/tools/track/fallback.mjs +0 -7
  545. package/tools/track/fk-parity-fixture.mjs +0 -161
  546. package/tools/track/gate.mjs +0 -349
  547. package/tools/track/masks.mjs +0 -318
  548. package/tools/track/metrics.mjs +0 -201
  549. package/tools/track/publish-obs.mjs +0 -108
  550. package/tools/track/py/check_env.py +0 -61
  551. package/tools/track/py/eval_lr.py +0 -229
  552. package/tools/track/py/lr_viterbi.py +0 -294
  553. package/tools/track/py/masks.py +0 -405
  554. package/tools/track/py/objective.py +0 -382
  555. package/tools/track/py/rig.py +0 -508
  556. package/tools/track/py/scene.py +0 -326
  557. package/tools/track/py/test_joint_indices.py +0 -123
  558. package/tools/track/py/test_lr_viterbi.py +0 -195
  559. package/tools/track/py/test_masks.py +0 -229
  560. package/tools/track/py/test_rig.py +0 -288
  561. package/tools/track/py/test_scene.py +0 -354
  562. package/tools/track/py/test_track.py +0 -328
  563. package/tools/track/py/track.py +0 -411
  564. package/tools/track/remote.mjs +0 -140
  565. package/tools/track/rig-dump.mjs +0 -227
  566. package/tools/track/run-box-tests.mjs +0 -14
  567. package/tools/track/run-box.mjs +0 -168
  568. package/tools/track/setup-box.sh +0 -59
  569. package/tools/track/study-2d.mjs +0 -728
  570. /package/src/{ardy → motion}/collision-blockers.js +0 -0
  571. /package/src/{ardy → motion}/ground.js +0 -0
  572. /package/src/{ardy → motion}/ik-drag.js +0 -0
  573. /package/src/{ardy → motion}/ik-key-json.js +0 -0
  574. /package/src/{ardy → motion}/ik.js +0 -0
  575. /package/src/{ardy → motion}/physics-surface.js +0 -0
  576. /package/src/{ardy → motion}/playback-clock.js +0 -0
  577. /package/src/{ardy → motion}/range-pin.js +0 -0
@@ -1,306 +0,0 @@
1
- #!/usr/bin/env python3
2
- """setup-text-encoder.py - provision the ARDY text-encoder models without a
3
- Hugging Face account, approval wait, or access token.
4
-
5
- ARDY's text encoder is LLM2Vec on top of meta-llama/Meta-Llama-3-8B-Instruct.
6
- The official Meta repository is gated (manual approval + token). This script
7
- instead downloads a byte-pinned stack from public, ungated repositories:
8
-
9
- base NousResearch/Meta-Llama-3-8B-Instruct (public mirror of the
10
- official weights; every safetensors shard is verified against a
11
- pinned SHA-256 before use)
12
- adapters McGill-NLP/LLM2Vec-Meta-Llama-3-8B-Instruct-mntp
13
- McGill-NLP/LLM2Vec-Meta-Llama-3-8B-Instruct-mntp-supervised
14
-
15
- and lays them out for ARDY's `TEXT_ENCODERS_DIR` override
16
- (ardy/model/llm2vec/llm2vec_wrapper.py), rewriting each adapter's
17
- `base_model_name_or_path` to the local base directory so nothing ever
18
- resolves back to the gated meta-llama repository at runtime.
19
-
20
- Every revision is pinned to a full commit SHA and every LFS payload to a
21
- SHA-256, so re-runs are reproducible and a tampered or changed upstream file
22
- fails loudly instead of silently drifting the embeddings (and with them the
23
- generated motion).
24
-
25
- Licenses: the base weights are Meta Llama 3 Community License (the LICENSE
26
- and USE_POLICY.md files are downloaded alongside the weights and must stay
27
- there); the McGill adapters are MIT. This script only downloads for local
28
- use - it does not redistribute anything.
29
-
30
- Usage (typically on the ARDY box; stdlib only, python >= 3.8):
31
-
32
- python3 setup-text-encoder.py # into ~/cclay-text-encoders
33
- python3 setup-text-encoder.py --dest /srv/enc # custom destination
34
- python3 setup-text-encoder.py --verify-only # re-hash an existing tree
35
-
36
- Afterwards start the encoder service with the printed environment:
37
-
38
- TEXT_ENCODERS_DIR=<dest> python scripts/run_text_encoder_server.py
39
- """
40
-
41
- import argparse
42
- import hashlib
43
- import json
44
- import os
45
- import sys
46
- import time
47
- import urllib.error
48
- import urllib.request
49
-
50
- HF = "https://huggingface.co"
51
-
52
- BASE_DIR = "NousResearch/Meta-Llama-3-8B-Instruct"
53
- MNTP_DIR = "McGill-NLP/LLM2Vec-Meta-Llama-3-8B-Instruct-mntp"
54
- # The mntp *download* lands in a -src dir: merge-text-encoder.py bakes the
55
- # base+MNTP merge into the runtime MNTP_DIR path (transformers v5 dropped
56
- # automatic adapter-dir resolution, so the runtime path must be a full model).
57
- MNTP_SRC_DIR = MNTP_DIR + "-src"
58
- SUP_DIR = "McGill-NLP/LLM2Vec-Meta-Llama-3-8B-Instruct-mntp-supervised"
59
-
60
- # (local dir, repo id, pinned full commit SHA, files)
61
- # Each file: (path, size, sha256-or-None). SHA-256 comes from the HF LFS
62
- # metadata of the pinned revision; small non-LFS files carry only a size and
63
- # are already immutable because the revision is a commit hash.
64
- # Pins match nv-tlabs/ardy issue #9's reproduction stack.
65
- MANIFEST = [
66
- (
67
- BASE_DIR,
68
- "NousResearch/Meta-Llama-3-8B-Instruct",
69
- "53346005fb0ef11d3b6a83b12c895cca40156b6c",
70
- [
71
- ("LICENSE", 7801, None),
72
- ("USE_POLICY.md", 4696, None),
73
- ("config.json", 654, None),
74
- ("generation_config.json", 187, None),
75
- ("model-00001-of-00004.safetensors", 4976698672,
76
- "d8cf9c4d0dd972e1a2131bfe656235ee98221679711a3beef6d46dadf0f20b5c"),
77
- ("model-00002-of-00004.safetensors", 4999802720,
78
- "8d4782b4a69ef03845159ce1a15e272aadaaf134dc138d68f616098e8531729c"),
79
- ("model-00003-of-00004.safetensors", 4915916176,
80
- "3acdd690e65c24f42a24581b8467af98bd3ca357444580f8012aacd2bd607921"),
81
- ("model-00004-of-00004.safetensors", 1168138808,
82
- "67e9ad31c8c32abf3a55ee7fc7217b3ecb35fd3c74d98a5bd233e0e4d6964f46"),
83
- ("model.safetensors.index.json", 23950, None),
84
- ("special_tokens_map.json", 73, None),
85
- ("tokenizer.json", 9085698, None),
86
- ("tokenizer_config.json", 50977, None),
87
- ],
88
- ),
89
- (
90
- MNTP_SRC_DIR,
91
- "McGill-NLP/LLM2Vec-Meta-Llama-3-8B-Instruct-mntp",
92
- "31474e395ada192e8ed1586db6be79fb3b70c9c0",
93
- [
94
- ("README.md", 3315, None),
95
- ("adapter_config.json", 794, None),
96
- ("adapter_model.safetensors", 167829552,
97
- "de9c8736618a13173c6a1623cdef1b75e86c69317f1073ae82cd516ac36a632d"),
98
- ("attn_mask_utils.py", 11132, None),
99
- ("config.json", 781, None),
100
- ("modeling_llama_encoder.py", 2856, None),
101
- ("special_tokens_map.json", 335, None),
102
- ("tokenizer.json", 9085671, None),
103
- ("tokenizer_config.json", 51042, None),
104
- ],
105
- ),
106
- (
107
- SUP_DIR,
108
- "McGill-NLP/LLM2Vec-Meta-Llama-3-8B-Instruct-mntp-supervised",
109
- "baa8ebf04a1c2500e61288e7dad65e8ae42601a7",
110
- [
111
- ("README.md", 62702, None),
112
- ("adapter_config.json", 794, None),
113
- ("adapter_model.safetensors", 167829552,
114
- "53f8f94ebdf396667ba99dd96e78203edae27bbcdbd1cf5f12b611e1d916b225"),
115
- ],
116
- ),
117
- ]
118
-
119
- # adapter_config.json is rewritten after download (base_model_name_or_path ->
120
- # local base dir), so its byte size legitimately changes; everything else must
121
- # keep the manifest size forever.
122
- MUTATED = {"adapter_config.json"}
123
-
124
- CHUNK = 1024 * 1024
125
-
126
-
127
- def log(msg):
128
- print("setup-text-encoder: %s" % msg, flush=True)
129
-
130
-
131
- def fail(msg):
132
- print("setup-text-encoder: ERROR: %s" % msg, file=sys.stderr, flush=True)
133
- sys.exit(1)
134
-
135
-
136
- def sha256_file(path):
137
- h = hashlib.sha256()
138
- with open(path, "rb") as f:
139
- while True:
140
- block = f.read(CHUNK)
141
- if not block:
142
- break
143
- h.update(block)
144
- return h.hexdigest()
145
-
146
-
147
- def file_ok(path, size, sha, verify_hash):
148
- """Is the on-disk file already the pinned artifact?"""
149
- if not os.path.isfile(path):
150
- return False
151
- name = os.path.basename(path)
152
- if name in MUTATED:
153
- return True # rewritten in place; checked by rewrite_adapter_config
154
- if os.path.getsize(path) != size:
155
- return False
156
- if sha is not None and verify_hash:
157
- return sha256_file(path) == sha
158
- return True
159
-
160
-
161
- def download(url, dest, size, sha):
162
- """Resumable download to dest + '.partial', verified, then atomic rename."""
163
- part = dest + ".partial"
164
- os.makedirs(os.path.dirname(dest), exist_ok=True)
165
- for attempt in range(1, 6):
166
- try:
167
- have = os.path.getsize(part) if os.path.isfile(part) else 0
168
- if have > size:
169
- os.remove(part)
170
- have = 0
171
- if have < size:
172
- req = urllib.request.Request(url)
173
- req.add_header("User-Agent", "cozyclay-setup-text-encoder/1")
174
- if have:
175
- req.add_header("Range", "bytes=%d-" % have)
176
- with urllib.request.urlopen(req, timeout=60) as resp:
177
- if have and resp.status != 206:
178
- # Server ignored the Range header; start over.
179
- have = 0
180
- mode = "wb"
181
- else:
182
- mode = "ab" if have else "wb"
183
- done = have
184
- t0 = time.time()
185
- with open(part, mode) as out:
186
- while True:
187
- block = resp.read(CHUNK)
188
- if not block:
189
- break
190
- out.write(block)
191
- done += len(block)
192
- if size > CHUNK and time.time() - t0 > 5:
193
- log(" ... %d / %d MiB" % (done // 2**20, size // 2**20))
194
- t0 = time.time()
195
- got = os.path.getsize(part)
196
- if got != size:
197
- raise IOError("size mismatch: got %d, want %d" % (got, size))
198
- if sha is not None:
199
- actual = sha256_file(part)
200
- if actual != sha:
201
- os.remove(part)
202
- raise IOError("sha256 mismatch: got %s, want %s" % (actual, sha))
203
- os.replace(part, dest)
204
- return
205
- except (urllib.error.URLError, IOError, OSError) as e:
206
- if attempt == 5:
207
- fail("giving up on %s after 5 attempts: %s" % (url, e))
208
- log(" retry %d/5 for %s (%s)" % (attempt, os.path.basename(dest), e))
209
- time.sleep(2 * attempt)
210
-
211
-
212
- def rewrite_adapter_config(path, base_path):
213
- """Point the adapter at the local base so nothing resolves to gated repos."""
214
- with open(path, "r", encoding="utf-8") as f:
215
- cfg = json.load(f)
216
- if cfg.get("base_model_name_or_path") == base_path:
217
- return False
218
- cfg["base_model_name_or_path"] = base_path
219
- tmp = path + ".tmp"
220
- with open(tmp, "w", encoding="utf-8") as f:
221
- json.dump(cfg, f, indent=2)
222
- f.write("\n")
223
- os.replace(tmp, path)
224
- return True
225
-
226
-
227
- def main():
228
- ap = argparse.ArgumentParser(description=__doc__.splitlines()[0])
229
- ap.add_argument("--dest", default=os.environ.get(
230
- "TEXT_ENCODERS_DIR", os.path.expanduser("~/cclay-text-encoders")),
231
- help="target directory (default: $TEXT_ENCODERS_DIR or ~/cclay-text-encoders)")
232
- ap.add_argument("--verify-only", action="store_true",
233
- help="verify an existing tree (sizes + SHA-256), download nothing")
234
- ap.add_argument("--print-plan", action="store_true",
235
- help="list every pinned file and exit")
236
- args = ap.parse_args()
237
-
238
- dest = os.path.abspath(os.path.expanduser(args.dest))
239
- os.makedirs(dest, exist_ok=True)
240
- dest_real = os.path.realpath(dest)
241
-
242
- def safe_join(*parts):
243
- """Join paths and verify the result stays within base."""
244
- full = os.path.realpath(os.path.join(dest_real, *parts))
245
- if full != dest_real and not full.startswith(dest_real + os.sep):
246
- fail("path traversal detected: %s" % os.path.join(*parts))
247
- return full
248
-
249
- base_path = safe_join(BASE_DIR)
250
-
251
- if args.print_plan:
252
- for local_dir, repo, rev, files in MANIFEST:
253
- print("%s @ %s" % (repo, rev))
254
- for path, size, sha in files:
255
- print(" %-40s %12d %s" % (path, size, sha or "-"))
256
- return
257
-
258
- total = sum(size for _, _, _, files in MANIFEST for _, size, _ in files)
259
- missing = []
260
- for local_dir, repo, rev, files in MANIFEST:
261
- for path, size, sha in files:
262
- target = safe_join(local_dir, path)
263
- if not file_ok(target, size, sha, verify_hash=args.verify_only):
264
- missing.append((local_dir, repo, rev, path, size, sha))
265
-
266
- if args.verify_only:
267
- if missing:
268
- for local_dir, _, _, path, _, _ in missing:
269
- print(" BAD/MISSING %s/%s" % (local_dir, path))
270
- fail("%d file(s) failed verification under %s" % (len(missing), dest))
271
- log("all %d files verified OK under %s" % (
272
- sum(len(f) for _, _, _, f in MANIFEST), dest))
273
- return
274
-
275
- if missing:
276
- todo = sum(m[4] for m in missing)
277
- log("destination: %s" % dest)
278
- log("downloading %d file(s), %.1f GiB of %.1f GiB total pinned payload"
279
- % (len(missing), todo / 2**30, total / 2**30))
280
- for local_dir, repo, rev, path, size, sha in missing:
281
- url = "%s/%s/resolve/%s/%s?download=true" % (HF, repo, rev, path)
282
- log("fetch %s/%s (%d bytes)" % (local_dir, path, size))
283
- download(url, safe_join(local_dir, path), size, sha)
284
- else:
285
- log("all pinned files already present under %s" % dest)
286
-
287
- for adapter_dir in (MNTP_SRC_DIR, SUP_DIR):
288
- cfg = safe_join(adapter_dir, "adapter_config.json")
289
- if rewrite_adapter_config(cfg, base_path):
290
- log("repointed %s -> local base" % os.path.join(adapter_dir, "adapter_config.json"))
291
-
292
- log("downloads verified. Bake the MNTP merge next (one-time, ~5 min):")
293
- log(" cd <ardy checkout> && <ardy venv python> merge-text-encoder.py --dest %s" % dest)
294
- print()
295
- print("Start ARDY's text-encoder service (and any local ARDY run) with:")
296
- print()
297
- print(" export TEXT_ENCODERS_DIR=%s" % dest)
298
- print(" python scripts/run_text_encoder_server.py")
299
- print()
300
- print("The base weights are Meta Llama 3 (Meta Llama 3 Community License;")
301
- print("see %s/LICENSE)." % base_path)
302
- print("This product is built with Meta Llama 3.")
303
-
304
-
305
- if __name__ == "__main__":
306
- main()
@@ -1,178 +0,0 @@
1
- /** Rotation-only GVHMR SMPL -> cskel27 retarget.
2
- * The argument is the member map returned by readNpz; plain arrays are also
3
- * accepted for small callers. Arrays may be nested or flat typed buffers.
4
- *
5
- * PROPORTIONS. Mocap is the product here, so the take carries the filmed
6
- * performer's body, not the canonical one: every cskel27 bone is scaled by
7
- * the ratio of the performer's SMPL rest bone length to the canonical bone
8
- * (`boneScale`, one factor per joint = the length of the bone ENDING at that
9
- * joint). posedJoints are grown with FK over those scaled offsets, so foot
10
- * placement, reach and stride are the performer's; playback applies the same
11
- * factors to the bones it drives by rotation only (the arm chain). A bone the
12
- * SMPL skeleton does not segment the same way (Spine..Spine3 are four links
13
- * against SMPL's three; hand ends; thumb) takes the nearest measured chain's
14
- * ratio so the body stays continuous. rootPos is the performer's pelvis
15
- * height as filmed: with the legs at their real length there is no height
16
- * ratio left to apply, and personScale is 1 by construction.
17
- */
18
- import { CSKEL27_JOINTS, CSKEL27_PARENTS } from "../../src/ardy/cskel27.js";
19
- import { deriveBoneOffsets, forwardKinematics, matMul, matTranspose } from "../../src/ardy/convert.js";
20
- import { canonicalCskel27Reference } from "../../src/ardy/to-cskel27.js";
21
-
22
- const SMPL_PARENTS = [-1, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 9, 9, 12, 13, 14, 16, 17, 18, 19, 20, 21];
23
- const IDENTITY = [[1, 0, 0], [0, 1, 0], [0, 0, 1]];
24
- const MAP = {
25
- Hips: 0, Spine: 3, Spine1: 6, Spine2: 9, Spine3: 9, Neck: 12, Head: 15,
26
- RightShoulder: 14, RightArm: 17, RightForeArm: 19, RightHand: 21,
27
- LeftShoulder: 13, LeftArm: 16, LeftForeArm: 18, LeftHand: 20,
28
- RightUpLeg: 2, RightLeg: 5, RightFoot: 8, RightToeBase: 11,
29
- LeftUpLeg: 1, LeftLeg: 4, LeftFoot: 7, LeftToeBase: 10,
30
- };
31
- // Limb rest directions are meaningful (SMPL's A-pose versus cskel27's
32
- // T-pose), so a constant swing aligns those chains without losing source
33
- // twist. Torso, pelvis, neck and head are deliberately absent: SMPL's joint
34
- // regressor forms an anatomical S inside the mesh and its head joint sits
35
- // forward inside the skull. Those are not visible-pose corrections and must
36
- // not deform the target character's bind shape.
37
- const CHILD = {
38
- RightShoulder: 17, RightArm: 19, RightForeArm: 21, RightHand: 23,
39
- LeftShoulder: 16, LeftArm: 18, LeftForeArm: 20, LeftHand: 22,
40
- RightUpLeg: 5, RightLeg: 8, RightFoot: 11,
41
- LeftUpLeg: 4, LeftLeg: 7, LeftFoot: 10,
42
- };
43
- const BORROW = { RightHandEnd: "RightHand", RightHandThumb1: "RightHand", LeftHandEnd: "LeftHand", LeftHandThumb1: "LeftHand" };
44
- const CORRECTION_BORROW = { RightToeBase: "RightFoot", LeftToeBase: "LeftFoot" };
45
- // Bone lengths measured on the performer, keyed by the cskel27 joint the bone
46
- // ENDS at, as [SMPL from, SMPL to] against [cskel27 from, cskel27 to]. Where
47
- // the two skeletons segment a limb differently the measurement spans the
48
- // whole segment on both sides and every link in it takes the one ratio:
49
- // - torso: SMPL's three spine joints do not sit where cskel27's four do, so
50
- // pelvis -> spine3 is one measurement over Spine..Spine3;
51
- // - shoulder girdle: SMPL's collar is a short stub (8 cm) where Mixamo's
52
- // clavicle is 16 cm, so spine3 -> shoulder covers Shoulder+Arm together
53
- // (per-link it read 0.79 / 0.57 and would have pinched the shoulders);
54
- // - neck, head, hand ends, thumb: geometry of the character, not a limb
55
- // the mocap measures. cskel27's Neck link (25 cm) already lands on the
56
- // Mixamo rig at 16 cm — the skin's neck is short by construction — so
57
- // scaling it by SMPL's 0.84 sank the neck into the chest (measured:
58
- // Spine2->Neck 16.5 -> 12.5 cm on the rig). Left at 1 like the head.
59
- const LENGTH_SOURCE = {
60
- Spine: [0, 9], Spine1: [0, 9], Spine2: [0, 9], Spine3: [0, 9],
61
- RightShoulder: [9, 17], RightArm: [9, 17], RightForeArm: [17, 19], RightHand: [19, 21],
62
- LeftShoulder: [9, 16], LeftArm: [9, 16], LeftForeArm: [16, 18], LeftHand: [18, 20],
63
- RightUpLeg: [0, 2], RightLeg: [2, 5], RightFoot: [5, 8], RightToeBase: [8, 11],
64
- LeftUpLeg: [0, 1], LeftLeg: [1, 4], LeftFoot: [4, 7], LeftToeBase: [7, 10],
65
- };
66
- const LENGTH_TARGET = {
67
- Spine: ["Hips", "Spine3"], Spine1: ["Hips", "Spine3"], Spine2: ["Hips", "Spine3"], Spine3: ["Hips", "Spine3"],
68
- RightShoulder: ["Spine3", "RightArm"], RightArm: ["Spine3", "RightArm"],
69
- LeftShoulder: ["Spine3", "LeftArm"], LeftArm: ["Spine3", "LeftArm"],
70
- };
71
-
72
- function data(value) {
73
- if (value && value.data !== undefined) return value.data;
74
- if (Array.isArray(value)) return value.flat(Infinity);
75
- return value;
76
- }
77
- function shape(value, expected, label) {
78
- const d = data(value);
79
- if (!d || d.length !== expected.reduce((a, b) => a * b, 1)) throw new Error(`${label} has the wrong size`);
80
- return d;
81
- }
82
- function vec(a) { const n = Math.hypot(...a); return n > 1e-12 ? a.map((v) => v / n) : [1, 0, 0]; }
83
- function cross(a, b) { return [a[1] * b[2] - a[2] * b[1], a[2] * b[0] - a[0] * b[2], a[0] * b[1] - a[1] * b[0]]; }
84
- function dot(a, b) { return a[0] * b[0] + a[1] * b[1] + a[2] * b[2]; }
85
- function axisAngle(axis, angle) {
86
- const [x, y, z] = axis, s = Math.sin(angle), c = Math.cos(angle), t = 1 - c;
87
- return [[t * x * x + c, t * x * y - s * z, t * x * z + s * y], [t * x * y + s * z, t * y * y + c, t * y * z - s * x], [t * x * z - s * y, t * y * z + s * x, t * z * z + c]];
88
- }
89
- function axisAngleMat(v) {
90
- const angle = Math.hypot(...v); return angle < 1e-12 ? IDENTITY.map((r) => r.slice()) : axisAngle(v.map((x) => x / angle), angle);
91
- }
92
- function swing(from, to) {
93
- const a = vec(from), b = vec(to), c = Math.max(-1, Math.min(1, dot(a, b)));
94
- if (c > 1 - 1e-10) return IDENTITY.map((r) => r.slice());
95
- let ax = cross(a, b), n = Math.hypot(...ax);
96
- if (n < 1e-10) {
97
- ax = Math.abs(a[0]) < 0.9 ? cross(a, [1, 0, 0]) : cross(a, [0, 1, 0]); n = Math.hypot(...ax);
98
- return axisAngle(ax.map((x) => x / n), Math.PI);
99
- }
100
- return axisAngle(ax.map((x) => x / n), Math.acos(c));
101
- }
102
- function readAa(flat, frame, joint) { const o = (frame * 23 + joint - 1) * 3; return [flat[o], flat[o + 1], flat[o + 2]]; }
103
- function sourceGlobals(go, pose, frames) {
104
- const worlds = new Array(frames);
105
- for (let f = 0; f < frames; f += 1) {
106
- const out = new Array(24); out[0] = axisAngleMat([go[f * 3], go[f * 3 + 1], go[f * 3 + 2]]);
107
- for (let j = 1; j < 24; j += 1) out[j] = matMul(out[SMPL_PARENTS[j]], axisAngleMat(readAa(pose, f, j)));
108
- worlds[f] = out;
109
- }
110
- return worlds;
111
- }
112
- function writeMat(out, offset, m) { out[offset] = m[0][0]; out[offset + 1] = m[0][1]; out[offset + 2] = m[0][2]; out[offset + 3] = m[1][0]; out[offset + 4] = m[1][1]; out[offset + 5] = m[1][2]; out[offset + 6] = m[2][0]; out[offset + 7] = m[2][1]; out[offset + 8] = m[2][2]; }
113
- function percentile(values, p) { const a = [...values].sort((x, y) => x - y); const x = (a.length - 1) * p, i = Math.floor(x), t = x - i; return a[i] * (1 - t) + (a[i + 1] ?? a[i]) * t; }
114
-
115
- export function smplToCskel27Motion(members, { boneScale: boneScaleOption = "derive" } = {}) {
116
- if (boneScaleOption !== "derive" && !(typeof boneScaleOption === "number" && Number.isFinite(boneScaleOption) && boneScaleOption > 0)) throw new Error('smplToCskel27Motion: boneScale must be "derive" or a positive number');
117
- const go = data(members.smpl_global_orient), poseMember = members.smpl_body_pose;
118
- const goShape = poseMember?.shape?.[0] ?? members.smpl_global_orient?.shape?.[0];
119
- const frames = Number.isInteger(goShape) ? goShape : go.length / 3;
120
- if (!Number.isInteger(frames) || frames < 1) throw new Error("smplToCskel27Motion: invalid frame count");
121
- shape(go, [frames, 3], "smpl_global_orient");
122
- const pose = shape(poseMember, [frames, 23, 3], "smpl_body_pose");
123
- const transl = shape(members.smpl_transl, [frames, 3], "smpl_transl");
124
- const rest = shape(members.smpl_rest_joints, [24, 3], "smpl_rest_joints");
125
- const fpsValue = data(members.fps); const fps = Number(fpsValue?.[0] ?? fpsValue);
126
- if (!(fps > 0)) throw new Error("smplToCskel27Motion: invalid fps");
127
- const skeleton = canonicalCskel27Reference();
128
- const cpos = skeleton.posed_joints;
129
- const restVec = (j) => [rest[j * 3], rest[j * 3 + 1], rest[j * 3 + 2]];
130
- const dist = (a, b) => Math.hypot(a[0] - b[0], a[1] - b[1], a[2] - b[2]);
131
- const boneScale = new Float32Array(27).fill(typeof boneScaleOption === "number" ? boneScaleOption : 1);
132
- for (let j = 1; j < 27 && boneScaleOption === "derive"; j += 1) {
133
- const name = CSKEL27_JOINTS[j];
134
- if (!LENGTH_SOURCE[name]) continue;
135
- const [s0, s1] = LENGTH_SOURCE[name];
136
- const source = dist(restVec(s0), restVec(s1));
137
- const [t0, t1] = LENGTH_TARGET[name] ?? [CSKEL27_JOINTS[CSKEL27_PARENTS[j]], name];
138
- const target = dist(cpos[CSKEL27_JOINTS.indexOf(t0)], cpos[CSKEL27_JOINTS.indexOf(t1)]);
139
- if (source > 1e-6 && target > 1e-6) boneScale[j] = source / target;
140
- }
141
- const offsets = deriveBoneOffsets(cpos, skeleton.local_rot_mats).map((o, j) => [o[0] * boneScale[j], o[1] * boneScale[j], o[2] * boneScale[j]]);
142
- const corrections = {};
143
- for (const [name, sourceChild] of Object.entries(CHILD)) {
144
- const c = CSKEL27_JOINTS.indexOf(name);
145
- const targetChild = CSKEL27_PARENTS.findIndex((parent) => parent === c);
146
- if (c < 0 || targetChild < 0) continue;
147
- const source = MAP[name];
148
- const a = [cpos[targetChild][0] - cpos[c][0], cpos[targetChild][1] - cpos[c][1], cpos[targetChild][2] - cpos[c][2]];
149
- const b = [rest[sourceChild * 3] - rest[source * 3], rest[sourceChild * 3 + 1] - rest[source * 3 + 1], rest[sourceChild * 3 + 2] - rest[source * 3 + 2]];
150
- corrections[name] = swing(a, b);
151
- }
152
- for (const [name, ancestor] of Object.entries(CORRECTION_BORROW)) corrections[name] = corrections[ancestor] ?? IDENTITY;
153
- const worlds = sourceGlobals(go, pose, frames), rotMats = new Float32Array(frames * 27 * 9), rootPos = new Float32Array(frames * 3), posedJoints = new Float32Array(frames * 27 * 3);
154
- // The legs now have the performer's length, so the pelvis rides at its
155
- // filmed height: no ratio between the two skeletons is left to apply.
156
- const rootScale = 1;
157
- for (let f = 0; f < frames; f += 1) {
158
- const globals = new Array(27);
159
- for (let j = 0; j < 27; j += 1) {
160
- const name = CSKEL27_JOINTS[j], ancestor = BORROW[name];
161
- // body_pose is already a rotation FROM SMPL's zero pose. Torso/head
162
- // therefore keep cskel27's bind shape; only visible limb rest-pose
163
- // differences use the constant correction built above.
164
- globals[j] = ancestor ? globals[CSKEL27_JOINTS.indexOf(ancestor)] : matMul(worlds[f][MAP[name]], corrections[name] ?? IDENTITY);
165
- }
166
- for (let j = 0; j < 27; j += 1) {
167
- const p = CSKEL27_PARENTS[j], local = p === null ? globals[j] : matMul(matTranspose(globals[p]), globals[j]);
168
- writeMat(rotMats, (f * 27 + j) * 9, local);
169
- }
170
- rootPos[f * 3] = transl[f * 3] * rootScale; rootPos[f * 3 + 1] = transl[f * 3 + 1] * rootScale; rootPos[f * 3 + 2] = transl[f * 3 + 2] * rootScale;
171
- }
172
- const readLocals = (f) => Array.from({ length: 27 }, (_, j) => { const o = (f * 27 + j) * 9; return [[rotMats[o], rotMats[o + 1], rotMats[o + 2]], [rotMats[o + 3], rotMats[o + 4], rotMats[o + 5]], [rotMats[o + 6], rotMats[o + 7], rotMats[o + 8]]]; });
173
- const lows = new Array(frames);
174
- for (let f = 0; f < frames; f += 1) { const p = forwardKinematics(readLocals(f), offsets, [rootPos[f * 3], rootPos[f * 3 + 1], rootPos[f * 3 + 2]]); lows[f] = Math.min(...[21, 22, 25, 26].map((j) => p[j][1])); }
175
- const floor = percentile(lows, 0.1);
176
- for (let f = 0; f < frames; f += 1) { rootPos[f * 3 + 1] -= floor; const p = forwardKinematics(readLocals(f), offsets, [rootPos[f * 3], rootPos[f * 3 + 1], rootPos[f * 3 + 2]]); for (let j = 0; j < 27; j += 1) posedJoints.set(p[j], (f * 27 + j) * 3); }
177
- return { frames, fps, rotMats, rootPos, posedJoints, boneScale, personScale: 1, rawRootStart: [transl[0], transl[1], transl[2]] };
178
- }
@@ -1,196 +0,0 @@
1
- /**
2
- * Visual QA driver for CozyClay: drives headless Chrome over CDP with
3
- * Node's built-in WebSocket. Loads the studio, generates "a person walk"
4
- * with the default seed (2), then captures screenshots at several playback
5
- * frames for pose review.
6
- *
7
- * Usage: node tools/ardy/visual-qa.mjs <outDir> [url]
8
- * The default URL is the studio route (`/app/`), not the marketing landing page.
9
- */
10
- import { writeFileSync, mkdirSync } from "node:fs";
11
-
12
- const OUT = process.argv[2] || "/tmp/visual-qa";
13
- const URL = process.argv[3] || "http://127.0.0.1:5180/app/";
14
- mkdirSync(OUT, { recursive: true });
15
-
16
- const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
17
-
18
- // --- minimal CDP client -----------------------------------------------------
19
- async function newTab() {
20
- const res = await fetch("http://127.0.0.1:9222/json/new?about:blank", { method: "PUT" });
21
- const target = await res.json();
22
- const ws = new WebSocket(target.webSocketDebuggerUrl);
23
- await new Promise((resolve, reject) => {
24
- ws.onopen = resolve;
25
- ws.onerror = reject;
26
- });
27
- let id = 0;
28
- const pending = new Map();
29
- const consoleLog = [];
30
- ws.onmessage = (event) => {
31
- const msg = JSON.parse(event.data);
32
- if (msg.id && pending.has(msg.id)) {
33
- const { resolve, reject } = pending.get(msg.id);
34
- pending.delete(msg.id);
35
- if (msg.error) reject(new Error(msg.error.message));
36
- else resolve(msg.result);
37
- } else if (msg.method === "Runtime.consoleAPICalled") {
38
- const type = msg.params.type;
39
- const text = msg.params.args.map((a) => a.value ?? a.description ?? "").join(" ");
40
- if (type === "error" || type === "warning") consoleLog.push(`[${type}] ${text}`);
41
- } else if (msg.method === "Runtime.exceptionThrown") {
42
- consoleLog.push(`[exception] ${msg.params.exceptionDetails.text} ${msg.params.exceptionDetails.exception?.description ?? ""}`);
43
- }
44
- };
45
- const send = (method, params = {}) =>
46
- new Promise((resolve, reject) => {
47
- const msgId = ++id;
48
- pending.set(msgId, { resolve, reject });
49
- ws.send(JSON.stringify({ id: msgId, method, params }));
50
- });
51
- return { ws, send, consoleLog };
52
- }
53
-
54
- async function evaluate(tab, expression) {
55
- const res = await tab.send("Runtime.evaluate", {
56
- expression,
57
- awaitPromise: true,
58
- returnByValue: true,
59
- });
60
- if (res.exceptionDetails) {
61
- throw new Error(`evaluate failed: ${JSON.stringify(res.exceptionDetails.exception?.description || res.exceptionDetails.text)}`);
62
- }
63
- return res.result.value;
64
- }
65
-
66
- async function shot(tab, name) {
67
- const res = await tab.send("Page.captureScreenshot", { format: "png" });
68
- writeFileSync(`${OUT}/${name}.png`, Buffer.from(res.data, "base64"));
69
- console.log(`saved ${OUT}/${name}.png`);
70
- }
71
-
72
- // --- flow -------------------------------------------------------------------
73
- const tab = await newTab();
74
- await tab.send("Page.enable");
75
- await tab.send("Runtime.enable");
76
- await tab.send("Page.navigate", { url: URL });
77
- await sleep(6000);
78
-
79
- // Prompt Blocks replaced the old single "Generate motion" field. Select the
80
- // first character, open the Prompt Blocks foldout, then add a block before
81
- // looking for its prompt input and the top-bar Generate Motion action.
82
- await evaluate(tab, `(() => {
83
- const character = [...document.querySelectorAll('.hierarchy-row')].find((row) => /Character 1|Subject 1|인물 1/.test(row.textContent));
84
- character?.click();
85
- const foldout = [...document.querySelectorAll('.foldout-head')].find((button) => /Prompt Blocks|프롬프트 블록/.test(button.textContent));
86
- foldout?.click();
87
- const add = document.querySelector('.tl-track.prompts .tl-track-add') || document.querySelector('.tl-track-add[title*="prompt"]');
88
- add?.click();
89
- return true;
90
- })()`);
91
- await sleep(400);
92
- const state0 = await evaluate(tab, `(() => {
93
- const seed = document.querySelector('input[placeholder="empty = random"]');
94
- const promptEl = document.querySelector('input[placeholder*="motion block"], input[placeholder*="모션 블록"]');
95
- const gen = document.querySelector('[data-testid=topbar-generate]');
96
- return { seedValue: seed?.value ?? null, hasGenerate: !!gen, generateDisabled: gen?.getAttribute('aria-disabled') === 'true', hasPrompt: !!promptEl };
97
- })()`);
98
- console.log("initial:", JSON.stringify(state0));
99
-
100
- if (!state0.hasGenerate || !state0.hasPrompt) {
101
- await shot(tab, "00-no-ui");
102
- throw new Error("studio UI not ready");
103
- }
104
- if (state0.seedValue === null) throw new Error("Prompt Blocks panel did not open");
105
-
106
- // Set the prompt and generate: focus the field, then type via the Input
107
- // domain so React's controlled input sees real key events.
108
- await evaluate(tab, `(() => {
109
- const el = document.querySelector('input[placeholder*="motion block"], input[placeholder*="모션 블록"]');
110
- if (!el) return false;
111
- el.focus();
112
- el.select?.();
113
- return true;
114
- })()`);
115
- for (const key of ["a", " ", "p", "e", "r", "s", "o", "n", " ", "w", "a", "l", "k"]) {
116
- await tab.send("Input.dispatchKeyEvent", { type: "keyDown", text: key });
117
- await tab.send("Input.dispatchKeyEvent", { type: "keyUp", text: key });
118
- }
119
- await sleep(300);
120
- await shot(tab, "01-before-generate");
121
- const clicked = await evaluate(tab, `(() => {
122
- const gen = document.querySelector('[data-testid=topbar-generate]');
123
- if (!gen || gen.getAttribute('aria-disabled') === 'true') return false;
124
- gen.click();
125
- return true;
126
- })()`);
127
- if (!clicked) throw new Error("Kimodo bridge is unavailable or generation is disabled");
128
- console.log("generate clicked, waiting for the clip...");
129
-
130
- // Wait for "Motion loaded" or the PLAYBACK badge (generation ~12 s).
131
- let loaded = false;
132
- for (let i = 0; i < 40; i += 1) {
133
- await sleep(1000);
134
- const st = await evaluate(tab, `document.body.textContent.includes("Motion loaded") || document.body.textContent.includes("PLAYBACK")`);
135
- if (st) { loaded = true; break; }
136
- }
137
- console.log("motion loaded:", loaded);
138
- if (!loaded) {
139
- await shot(tab, "02-generation-timeout");
140
- throw new Error("motion did not load within 40 s");
141
- }
142
- await sleep(1500);
143
-
144
- // Scrub to specific frames by clicking the timeline's accessible ruler. The
145
- // old tick-text selector clicked labels, not the scrub surface, after the
146
- // timeline was redesigned.
147
- const frames = [0, 20, 40, 60, 79];
148
- for (const f of frames) {
149
- const point = await evaluate(tab, `(() => {
150
- const lane = document.querySelector('[role="slider"][aria-label*="Scrub timeline"], [role="slider"][aria-label*="타임라인"]');
151
- if (!lane) return null;
152
- const rect = lane.getBoundingClientRect();
153
- const max = Math.max(1, Number(lane.getAttribute('aria-valuemax')) || 1);
154
- return { x: rect.left + rect.width * Math.min(1, ${f} / max), y: rect.top + rect.height / 2 };
155
- })()`);
156
- if (!point) throw new Error("timeline scrub ruler not found");
157
- await tab.send("Input.dispatchMouseEvent", { type: "mouseMoved", x: point.x, y: point.y, button: "none" });
158
- await tab.send("Input.dispatchMouseEvent", { type: "mousePressed", x: point.x, y: point.y, button: "left", clickCount: 1 });
159
- await tab.send("Input.dispatchMouseEvent", { type: "mouseReleased", x: point.x, y: point.y, button: "left", clickCount: 1 });
160
- await sleep(700);
161
- await shot(tab, `10-frame-${String(f).padStart(2, "0")}`);
162
- }
163
-
164
- // Play a couple of seconds for a mid-stride live shot.
165
- await evaluate(tab, `(() => {
166
- const play = [...document.querySelectorAll("button")].find(b => b.textContent.trim() === "▶");
167
- play?.click();
168
- return true;
169
- })()`);
170
- await sleep(1600);
171
- await shot(tab, "20-playing-midstride");
172
- await sleep(1600);
173
- await shot(tab, "21-playing-later");
174
- await evaluate(tab, `(() => {
175
- const pause = [...document.querySelectorAll("button")].find(b => /❚❚|⏸/.test(b.textContent.trim()));
176
- pause?.click();
177
- return true;
178
- })()`);
179
-
180
- // Camera close-up on the subject for a detailed pose check.
181
- await evaluate(tab, `(() => {
182
- const chip = [...document.querySelectorAll("button")].find(b => /close-up/i.test(b.textContent));
183
- chip?.click();
184
- return true;
185
- })()`);
186
- await sleep(900);
187
- await shot(tab, "30-closeup");
188
-
189
- if (tab.consoleLog.length) {
190
- console.log("--- console errors/warnings ---");
191
- for (const line of [...new Set(tab.consoleLog)].slice(0, 15)) console.log(line);
192
- } else {
193
- console.log("no console errors/warnings captured");
194
- }
195
- tab.ws.close();
196
- console.log("visual QA capture complete");