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,105 +0,0 @@
1
- // Pure request construction. The caller supplies one rolled seed and sampled
2
- // poses; no rig, store, random source, notification or network is touched here.
3
- import { StudioProtocolError, compileStudioBeats } from '../studio-agent-protocol.js';
4
- import { TIMELINE_FRAME_FPS as FPS } from '../timeline-fps.js';
5
- import { judgeAuthoredPath, alignArdyPath } from '../ardy/waypoints.js';
6
- import { planPosePin, PIN_BLOCKED } from '../ardy/pose-pin.js';
7
- import { blocksFromRequest, replayPayload, replayTruncated } from '../take-recipe.js';
8
-
9
- // Same half-open timeline schedule used by the editor: gaps carry the base
10
- // prompt, and spans shorter than three frames cannot carry a generation.
11
- function promptSchedule(clips, clipFrames, prompt) {
12
- const segments = [];
13
- let cursor = 0;
14
- for (const clip of clips) {
15
- const startFrame = Math.max(cursor, Math.min(clipFrames, clip.startFrame));
16
- const endFrame = Math.max(startFrame, Math.min(clipFrames, clip.endFrame));
17
- if (startFrame > cursor) segments.push({ startFrame: cursor, endFrame: startFrame, prompt });
18
- if (endFrame - startFrame >= 3) segments.push({ startFrame, endFrame, prompt: clip.text.trim() || prompt });
19
- cursor = Math.max(cursor, endFrame);
20
- if (cursor >= clipFrames) break;
21
- }
22
- if (cursor < clipFrames) segments.push({ startFrame: cursor, endFrame: clipFrames, prompt });
23
- if (!segments.length) segments.push({ startFrame: 0, endFrame: clipFrames, prompt });
24
- return segments;
25
- }
26
-
27
- export function generationArgs({ characterId, source }) {
28
- if (source.kind !== 'generate') throw generationRefusal('CAPABILITY_MISSING', 'Artifact reuse is handled by the retained artifact runtime.');
29
- const schedule = compileStudioBeats(source);
30
- return { characterId, durationSeconds: schedule.durationSeconds,
31
- blocks: schedule.blocks.map((block, index) => ({ id: `generation-block-${index}`, text: block.text, startFrame: block.startFrame, endFrame: block.endFrameExclusive })),
32
- ...(source.seed === undefined ? {} : { seed: source.seed }) };
33
- }
34
-
35
- export function generationRefusal(code, message, uiMessage = message) {
36
- return Object.assign(new StudioProtocolError(code, message), { uiMessage });
37
- }
38
-
39
- export function buildGenerationRequest({ character, prompt, durationSeconds, seed,
40
- waypoints = [], waypointMode = waypoints.length > 0, blocks = [], motion = null,
41
- ikKeys = new Map(), startFromPose = false, posePlacement = 'start', frame = 0, poses = [],
42
- preserveStrength = 0.5, recipe = null, fresh = false,
43
- }) {
44
- const text = String(prompt ?? '').trim();
45
- if (!text) throw generationRefusal('INVALID_ARGUMENT', 'Motion prompt is required - describe what the subject should do before generating');
46
- if (text.length > 500) throw generationRefusal('INVALID_ARGUMENT', `Motion prompt is capped at 500 characters (currently ${text.length}) - shorten it before generating`);
47
- const ikFrames = [...ikKeys.keys()].sort((a, b) => a - b);
48
- const duration = motion && ikFrames.length ? motion.frames / motion.fps : Number(durationSeconds);
49
- const clipFrames = Math.round(duration * FPS);
50
- if (!Number.isFinite(duration) || duration < 1 || duration > 1200) throw generationRefusal('INVALID_RANGE', 'Duration must be between 1 and 1200 seconds');
51
- if (!Number.isInteger(seed) || seed < 0 || seed > 2 ** 31 - 1) throw generationRefusal('INVALID_ARGUMENT', 'Seed must be an integer in 0..2147483647');
52
- const warnings = [];
53
- const clips = blocks.filter(clip => clip.text.trim()).sort((a, b) => a.startFrame - b.startFrame);
54
- const segments = promptSchedule(clips, clipFrames, text), hasPromptSchedule = segments.length > 1;
55
- const rootPath = waypointMode ? [{ frame: 0, x: character.x, z: character.z, heading: null }, ...waypoints] : [];
56
- if (waypointMode) {
57
- if (!waypoints.length) throw generationRefusal('INVALID_RANGE', 'Add at least one root destination before generating');
58
- if (rootPath.length > 32) throw generationRefusal('INVALID_RANGE', 'The root path is capped at 32 sparse waypoints');
59
- if (waypoints.some(point => point.frame <= 0 || point.frame >= clipFrames)) throw generationRefusal('INVALID_RANGE', `Root waypoint frames must stay inside 1..${clipFrames - 1}`);
60
- const verdict = judgeAuthoredPath(rootPath, FPS, clipFrames, { chained: hasPromptSchedule });
61
- if (verdict.errors.length) throw generationRefusal('INVALID_RANGE', `Not generated - ${verdict.errors[0]}`);
62
- warnings.push(...verdict.warnings);
63
- }
64
- const longBlock = clips.length && segments.find(segment => segment.endFrame - segment.startFrame > 5 * FPS);
65
- if (longBlock) throw generationRefusal('INVALID_RANGE', `Not generated - prompt blocks are capped at 5 s; split the ${((longBlock.endFrame - longBlock.startFrame) / FPS).toFixed(1)} s block`);
66
- if (segments.some(segment => segment.prompt.length > 500)) throw generationRefusal('INVALID_ARGUMENT', 'Each motion prompt block is capped at 500 characters');
67
- const aligned = waypointMode ? alignArdyPath(rootPath, character.rot, 32) : null;
68
- const editedSegments = motion?.url && hasPromptSchedule
69
- ? segments.filter(segment => ikFrames.some(at => at >= segment.startFrame && at < segment.endFrame)) : [];
70
- const hasBlockEdits = editedSegments.length > 0;
71
- const last = clipFrames - 1;
72
- const poseFrame = posePlacement === 'end' ? last : posePlacement === 'middle' ? Math.round(last / 2)
73
- : posePlacement === 'playhead' ? Math.max(0, Math.min(last, frame)) : 0;
74
- const plan = planPosePin({ startFromPose, poseFrame, hasPromptSchedule, hasBlockEdits, waypointMode, ikFrames, clipFrames, segments, editedSegments });
75
- if (plan.blockedBy === PIN_BLOCKED.SCHEDULE) warnings.push('Prompt blocks and a pose start cannot be combined - generating from the prompt alone.');
76
- const body = { prompt: text, duration, posePin: plan.pin, seed };
77
- let committedEditKeys = [];
78
- if (plan.pin && !hasBlockEdits) body.poses = poses.map(entry => ({ ...entry }));
79
- if (aligned) {
80
- Object.assign(body, { waypoints: aligned.waypoints, rootMargin: 0.08, historyFrames: 4 * FPS });
81
- if (hasPromptSchedule && !hasBlockEdits) body.segments = segments;
82
- } else if (hasBlockEdits) {
83
- const entries = plan.frames.map(at => ({ frame: at, tracks: [...(ikKeys.get(at)?.keys() ?? [])], pose: poses.find(entry => entry.frame === at)?.pose }));
84
- committedEditKeys = entries.map(({ frame, tracks }) => ({ frame, tracks }));
85
- body.motionEdit = { sourceMotion: motion.url, startFrame: Math.min(...editedSegments.map(segment => segment.startFrame)),
86
- endFrame: Math.max(...editedSegments.map(segment => segment.endFrame)), contextBefore: 40, contextAfter: 20, edits: entries };
87
- } else if (hasPromptSchedule) body.segments = segments;
88
- const requestBlocks = blocksFromRequest(body, FPS), recipeBlocks = recipe?.blocks;
89
- const durationFits = motion?.frames > 0 && Math.abs(motion.frames / FPS - duration) <= 1 / FPS + 1e-9;
90
- const promptMatches = body.motionEdit !== undefined || (recipeBlocks?.length === requestBlocks.length && recipeBlocks.every((block, i) => block.prompt.trim() === requestBlocks[i].prompt.trim()));
91
- if (!fresh && motion?.url && preserveStrength > 0 && durationFits && promptMatches && !body.segments) {
92
- body.preserve = { sourceMotion: motion.url, strength: preserveStrength,
93
- editRanges: editedSegments.map(({ startFrame, endFrame }) => {
94
- const tracks = [...new Set(ikFrames.filter(at => at >= startFrame && at < endFrame).flatMap(at => [...ikKeys.get(at).keys()]))];
95
- return { startFrame, endFrame, ...(tracks.length ? { tracks } : {}) };
96
- }).filter(range => range.endFrame > range.startFrame) };
97
- }
98
- const replay = fresh || hasBlockEdits || !Number.isInteger(recipe?.seed) ? [] : replayPayload(recipe);
99
- if (replay.length) {
100
- body.replay = replay;
101
- if (replayTruncated(recipe)) warnings.push(`Only ${replay.length} refinements can be replayed at once - the first ${replay.length} carry over`);
102
- }
103
- return { body, rootRotationDeg: aligned?.rotationDeg ?? character.rot, warnings,
104
- constraintFrames: plan.frames, hasBlockEdits, hasPromptSchedule, committedEditKeys };
105
- }
@@ -1,67 +0,0 @@
1
- import { ko } from "./locale.js";
2
-
3
- export function motionReadinessMessage(state) {
4
- switch (state) {
5
- case "loading": return ko("Checking motion generation…", "모션 생성 연결 확인 중…");
6
- case "ready": return ko("Ready for this motion request", "이 모션 요청을 실행할 수 있어요");
7
- case "not_configured": return ko("No motion backend configured", "모션 백엔드가 설정되지 않았어요");
8
- case "unsupported_route": return ko("This route cannot run the selected motion request", "현재 경로는 이 모션 요청을 지원하지 않아요");
9
- default: return ko("The motion backend is unavailable", "모션 백엔드를 사용할 수 없어요");
10
- }
11
- }
12
-
13
- export function MotionReadiness({ state, checking, onSetup, onRetry }) {
14
- const unavailable = state === "unavailable";
15
- return (
16
- <div className="motion-readiness" data-state={state} aria-busy={checking || state === "loading"}>
17
- <span role="status">{motionReadinessMessage(state)}</span>
18
- {state !== "ready" && state !== "loading" && (
19
- <button
20
- type="button"
21
- className="btn ghost"
22
- data-testid="motion-readiness-action"
23
- disabled={checking}
24
- onClick={unavailable ? onRetry : () => onSetup()}
25
- >
26
- {checking ? ko("Checking…", "확인 중…")
27
- : unavailable ? ko("Retry connection", "연결 다시 확인")
28
- : state === "unsupported_route" ? ko("Switch route in Settings", "설정에서 경로 변경")
29
- : ko("Open Settings", "설정 열기")}
30
- </button>
31
- )}
32
- </div>
33
- );
34
- }
35
-
36
- export function MotionSetup({ state, checking, onRetry }) {
37
- const localStudio = ["localhost", "127.0.0.1", "[::1]"].includes(window.location.hostname);
38
- const port = localStudio ? window.location.port || "5180" : "5180";
39
- return (
40
- <section className="motion-setup" data-testid="motion-setup" data-state={state} aria-label={ko("Motion generation setup", "모션 생성 설정")}>
41
- <p role="status">{motionReadinessMessage(state)}</p>
42
- <p>{ko(
43
- "Motion generation is optional. Sample motion, camera editing and exports work without it. Your scene and prompt blocks stay here while you set it up.",
44
- "모션 생성은 선택 사항이에요. 연결 없이도 샘플 모션, 카메라 편집, 내보내기를 쓸 수 있어요. 설정하는 동안 씬과 프롬프트 블록은 그대로 유지됩니다.",
45
- )}</p>
46
- <p>{ko("Use an existing motion generation host, then restart your local launcher with:", "기존 모션 생성 호스트를 사용하려면 로컬 실행기에 다음 환경변수를 설정하세요:")}</p>
47
- <code>{`CCLAY_KIMODO_HOST=user@your-gpu-box npx cozyclay --port ${port} --no-open`}</code>
48
- <p>{localStudio ? ko(
49
- "Keep this tab open. After restarting the bridge on the same port, retry the connection below.",
50
- "이 탭은 열어 두세요. 같은 포트에서 브리지를 다시 시작한 뒤 아래에서 연결을 확인하세요.",
51
- ) : ko(
52
- "New generation runs in the local Studio. Save your project here, then open that file in the local Studio to carry over your edits.",
53
- "새 모션 생성은 로컬 Studio에서 실행해요. 여기서 프로젝트를 저장하고 로컬 Studio에서 그 파일을 열면 편집 내용을 이어갈 수 있어요.",
54
- )}</p>
55
- <p>{ko(
56
- "Local MLX/cpp supports one unconstrained prompt. Sequencing, pinned poses, paths and preserve need the CUDA/SSH route. Line editing needs a configured line-edit route.",
57
- "로컬 MLX/cpp는 제약 없는 단일 프롬프트를 지원해요. 블록 연결, 포즈 고정, 경로, 테이크 보존에는 CUDA/SSH가 필요하고, 라인 편집에는 라인 편집 경로 설정이 필요해요.",
58
- )}</p>
59
- <a href="https://github.com/NomaDamas/CozyClay/blob/main/docs/kimodo-setup.md" target="_blank" rel="noreferrer">
60
- {ko("Motion generation setup and route guide", "모션 생성 설정 및 경로 안내")}
61
- </a>
62
- <button type="button" className="btn" data-testid="motion-health-retry" disabled={checking} onClick={onRetry}>
63
- {checking ? ko("Checking…", "확인 중…") : ko("Recheck connection", "연결 다시 확인")}
64
- </button>
65
- </section>
66
- );
67
- }
@@ -1,40 +0,0 @@
1
- import { motionPreflightReason } from "./analytics.js";
2
-
3
- /** Return the line-edit capability advertised by the structured health payload. */
4
- export function hasLineEditCapability(health) {
5
- if (health?.ok !== true) return false;
6
- const capabilities = health.capabilities ?? health.features;
7
- return Array.isArray(capabilities) ? capabilities.includes("lineEdit") : capabilities?.lineEdit === true;
8
- }
9
-
10
- /**
11
- * Decide whether the hosted demo take may be seeded onto the stage. The seed
12
- * is for a session with no bridge (the hosted demo). The first healthy probe
13
- * latches `bridgeSeenOk`: after it, a failed probe is a blip, not "no bridge",
14
- * and must never load the demo take onto the character.
15
- */
16
- export function demoSeedGate(health, bridgeSeenOk = false) {
17
- const seenOk = bridgeSeenOk || Boolean(health?.ok);
18
- return { bridgeSeenOk: seenOk, seed: Boolean(health) && !health.ok && !seenOk };
19
- }
20
-
21
- /**
22
- * Derive the generation affordance state from bridge health and request shape.
23
- * Health is deliberately interpreted through the analytics preflight contract;
24
- * error prose is never used to classify a route.
25
- */
26
- export function motionReadiness(health, options = {}) {
27
- if (health === null || health === undefined) return "loading";
28
- const lineEditSupported = Object.prototype.hasOwnProperty.call(options, "lineEditSupported")
29
- ? options.lineEditSupported === true
30
- : hasLineEditCapability(health);
31
- const reason = motionPreflightReason(health, {
32
- body: options.body ?? {},
33
- lineEditSupported,
34
- });
35
- if (reason === null) return "ready";
36
- if (reason === "unconfigured") return "not_configured";
37
- if (reason === "unreachable") return "unavailable";
38
- if (reason === "unsupported_route") return "unsupported_route";
39
- return "unavailable";
40
- }
@@ -1,177 +0,0 @@
1
- /**
2
- * Motion resources: a validated ARDY npz archive packaged so a `.cclayproject`
3
- * can carry the take itself instead of a bridge URL that dies with the run.
4
- *
5
- * A record is content-addressed: `motionId` is the SHA-256 of the raw archive
6
- * bytes, so a scene's `character.motionRef.motionId` names the exact bytes it
7
- * was authored against. Decoding re-derives the hash and re-runs the full npz
8
- * validation, so a tampered or truncated record is rejected before playback
9
- * ever sees it. Every rejection is an NpzError carrying a machine-readable
10
- * `code` (bad-base64 | length-mismatch | hash-mismatch | bad-npz | too-large).
11
- *
12
- * No React, no three.js, no fetch: this module runs identically in the
13
- * browser and under Node (Web Crypto + atob/btoa are available in both).
14
- */
15
-
16
- import { decodeMotionNpz, NpzError } from "./ardy/npz.js";
17
-
18
- /** Per-motion raw archive cap; matches the npz reader's download cap. */
19
- export const MOTION_MAX_BYTES = 192 * 1024 * 1024;
20
-
21
- const MOTION_ID_RE = /^[0-9a-f]{64}$/i;
22
- const BASE64_RE = /^[A-Za-z0-9+/]*={0,2}$/;
23
-
24
- function codedError(code, message) {
25
- const error = new NpzError(message);
26
- error.code = code;
27
- return error;
28
- }
29
-
30
- function asBytes(value) {
31
- if (value instanceof Uint8Array) return value;
32
- if (value instanceof ArrayBuffer) return new Uint8Array(value);
33
- if (ArrayBuffer.isView(value)) return new Uint8Array(value.buffer, value.byteOffset, value.byteLength);
34
- throw new TypeError("motion bytes must be a Uint8Array or ArrayBuffer");
35
- }
36
-
37
- /** Lowercase hex SHA-256 of raw bytes — the motionId of an archive. */
38
- export async function sha256Hex(value) {
39
- const bytes = asBytes(value);
40
- const subtle = globalThis.crypto?.subtle;
41
- if (!subtle) throw new Error("Web Crypto SHA-256 is unavailable");
42
- const digest = await subtle.digest("SHA-256", bytes);
43
- let hex = "";
44
- for (const byte of new Uint8Array(digest)) hex += byte.toString(16).padStart(2, "0");
45
- return hex;
46
- }
47
-
48
- function bytesToBase64(bytes) {
49
- let binary = "";
50
- for (let offset = 0; offset < bytes.length; offset += 0x8000) {
51
- binary += String.fromCharCode(...bytes.subarray(offset, offset + 0x8000));
52
- }
53
- return btoa(binary);
54
- }
55
-
56
- function base64ToBytes(text) {
57
- if (typeof text !== "string" || text.length % 4 !== 0 || !BASE64_RE.test(text)) {
58
- throw codedError("bad-base64", "motion resource data is not valid base64");
59
- }
60
- let binary;
61
- try {
62
- binary = atob(text);
63
- } catch {
64
- throw codedError("bad-base64", "motion resource data is not valid base64");
65
- }
66
- const bytes = new Uint8Array(binary.length);
67
- for (let i = 0; i < binary.length; i += 1) bytes[i] = binary.charCodeAt(i);
68
- return bytes;
69
- }
70
-
71
- /** Decoded byte count of a base64 string, without decoding it. */
72
- function base64DecodedLength(text) {
73
- let padding = 0;
74
- if (text.endsWith("==")) padding = 2;
75
- else if (text.endsWith("=")) padding = 1;
76
- return (text.length / 4) * 3 - padding;
77
- }
78
-
79
- const META_KEYS = ["prompt", "sourceUrl", "personScale", "createdAt"];
80
-
81
- /**
82
- * Validate an npz archive and package it as a project motion record:
83
- * { motionId, encoding: "base64", data, bytes, frames, fps, name?, meta? }.
84
- *
85
- * `meta` may carry `name` (kept top-level) and prompt / sourceUrl /
86
- * personScale / createdAt (kept under `meta`). personScale defaults to the
87
- * value the archive itself carries so the record is self-describing.
88
- */
89
- export async function encodeMotionResource(value, meta = {}) {
90
- const bytes = asBytes(value);
91
- if (bytes.byteLength > MOTION_MAX_BYTES) {
92
- throw codedError("too-large", `motion resource is ${bytes.byteLength} bytes, over the ${MOTION_MAX_BYTES} byte cap`);
93
- }
94
- let motion;
95
- try {
96
- motion = await decodeMotionNpz(bytes);
97
- } catch (error) {
98
- if (error instanceof NpzError && !error.code) error.code = "bad-npz";
99
- throw error;
100
- }
101
- const motionId = await sha256Hex(bytes);
102
- const record = {
103
- motionId,
104
- encoding: "base64",
105
- data: bytesToBase64(bytes),
106
- bytes: bytes.byteLength,
107
- frames: motion.frames,
108
- fps: motion.fps,
109
- };
110
- const source = meta && typeof meta === "object" ? meta : {};
111
- if (typeof source.name === "string" && source.name) record.name = source.name;
112
- const extra = { personScale: motion.personScale };
113
- for (const key of META_KEYS) {
114
- if (source[key] !== undefined && source[key] !== null) extra[key] = source[key];
115
- }
116
- record.meta = extra;
117
- return record;
118
- }
119
-
120
- /**
121
- * Unpack a motion record back into a decoded motion. Checks, in order: size
122
- * cap, base64 syntax, declared byte length, SHA-256 against motionId, then
123
- * the full npz validation. Returns the decodeMotionNpz result plus
124
- * { motionId, sourceBytes } — exactly what loadMotionFromUrl returns, so the
125
- * restore path treats an embedded take and a downloaded one the same way.
126
- */
127
- export async function decodeMotionResource(record) {
128
- if (!record || typeof record !== "object") {
129
- throw codedError("bad-base64", "motion resource must be an object with base64 data");
130
- }
131
- if (Number.isFinite(record.bytes) && record.bytes > MOTION_MAX_BYTES) {
132
- throw codedError("too-large", `motion resource declares ${record.bytes} bytes, over the ${MOTION_MAX_BYTES} byte cap`);
133
- }
134
- if (record.encoding !== "base64" || typeof record.data !== "string") {
135
- throw codedError("bad-base64", "motion resource must contain base64 data");
136
- }
137
- if (base64DecodedLength(record.data) > MOTION_MAX_BYTES) {
138
- throw codedError("too-large", `motion resource data decodes past the ${MOTION_MAX_BYTES} byte cap`);
139
- }
140
- const bytes = base64ToBytes(record.data);
141
- if (bytes.byteLength > MOTION_MAX_BYTES) {
142
- throw codedError("too-large", `motion resource is ${bytes.byteLength} bytes, over the ${MOTION_MAX_BYTES} byte cap`);
143
- }
144
- if (!Number.isInteger(record.bytes) || record.bytes !== bytes.byteLength) {
145
- throw codedError("length-mismatch", `motion resource declares ${record.bytes} bytes but carries ${bytes.byteLength}`);
146
- }
147
- const declaredId = typeof record.motionId === "string" && MOTION_ID_RE.test(record.motionId) ? record.motionId.toLowerCase() : null;
148
- const motionId = await sha256Hex(bytes);
149
- if (declaredId !== motionId) {
150
- throw codedError("hash-mismatch", "motion resource bytes do not match its motionId");
151
- }
152
- let motion;
153
- try {
154
- motion = await decodeMotionNpz(bytes);
155
- } catch (error) {
156
- if (error instanceof NpzError && !error.code) error.code = "bad-npz";
157
- throw error;
158
- }
159
- return { ...motion, motionId, sourceBytes: bytes };
160
- }
161
-
162
- /**
163
- * Where a character's motion should come from on restore. Embedded bytes win
164
- * over a URL: the URL is a bridge run that may be gone, the bytes are here.
165
- * { kind: "embedded", record } | { kind: "url", url } | { kind: "missing" }
166
- */
167
- export function resolveMotionSource(motionRef, motionsById) {
168
- if (!motionRef || typeof motionRef !== "object") return { kind: "missing" };
169
- if (typeof motionRef.motionId === "string" && motionsById && typeof motionsById.get === "function") {
170
- const record = motionsById.get(motionRef.motionId.toLowerCase());
171
- if (record) return { kind: "embedded", record };
172
- }
173
- if (typeof motionRef.url === "string" && motionRef.url) return { kind: "url", url: motionRef.url };
174
- return { kind: "missing" };
175
- }
176
-
177
- export { loadMotionFromUrl } from "./ardy/npz.js";
@@ -1,65 +0,0 @@
1
- const MOTION_DB_NAME = "cozyclay.motions";
2
- const MOTION_DB_VERSION = 1;
3
- const MOTION_STORE_NAME = "motions";
4
-
5
- const validId = (id) => typeof id === "string" && /^[0-9a-f]{64}$/i.test(id);
6
-
7
- function normalizeMotion(record) {
8
- if (!record || typeof record !== "object" || !validId(record.motionId)) return null;
9
- if (record.encoding !== "base64" || typeof record.data !== "string") return null;
10
- return { ...record, motionId: record.motionId.toLowerCase(), stored: true };
11
- }
12
-
13
- export function openMotionDb(factory = globalThis.indexedDB) {
14
- if (!factory) return Promise.reject(new Error("IndexedDB is unavailable"));
15
- return new Promise((resolve, reject) => {
16
- const req = factory.open(MOTION_DB_NAME, MOTION_DB_VERSION);
17
- req.onupgradeneeded = () => {
18
- if (!req.result.objectStoreNames.contains(MOTION_STORE_NAME)) req.result.createObjectStore(MOTION_STORE_NAME, { keyPath: "motionId" });
19
- };
20
- req.onsuccess = () => resolve(req.result);
21
- req.onerror = () => reject(req.error);
22
- req.onblocked = () => reject(new Error("the motion database is blocked by another tab"));
23
- });
24
- }
25
-
26
- const request = (req) => new Promise((resolve, reject) => {
27
- req.onsuccess = () => resolve(req.result);
28
- req.onerror = () => reject(req.error);
29
- });
30
- const complete = (tx) => new Promise((resolve, reject) => {
31
- tx.oncomplete = resolve;
32
- tx.onerror = () => reject(tx.error);
33
- tx.onabort = () => reject(tx.error);
34
- });
35
-
36
- export async function putMotion(db, record) {
37
- const value = normalizeMotion(record);
38
- if (!value) throw new TypeError("putMotion needs a valid motion record");
39
- const tx = db.transaction(MOTION_STORE_NAME, "readwrite");
40
- tx.objectStore(MOTION_STORE_NAME).put(value);
41
- await complete(tx);
42
- return value;
43
- }
44
- export async function getMotion(db, id) {
45
- if (!validId(id)) return null;
46
- const tx = db.transaction(MOTION_STORE_NAME, "readonly");
47
- return normalizeMotion(await request(tx.objectStore(MOTION_STORE_NAME).get(id.toLowerCase())));
48
- }
49
- export async function listMotionIds(db) {
50
- const tx = db.transaction(MOTION_STORE_NAME, "readonly");
51
- return (await request(tx.objectStore(MOTION_STORE_NAME).getAllKeys()) ?? []).filter(validId).map((id) => id.toLowerCase());
52
- }
53
- export async function deleteMotion(db, id) {
54
- if (!validId(id)) return false;
55
- const tx = db.transaction(MOTION_STORE_NAME, "readwrite");
56
- tx.objectStore(MOTION_STORE_NAME).delete(id.toLowerCase());
57
- await complete(tx);
58
- return true;
59
- }
60
- export async function sweepMotions(db, referencedIds) {
61
- const keep = new Set([...referencedIds ?? []].map((id) => String(id).toLowerCase()));
62
- const ids = await listMotionIds(db);
63
- await Promise.all(ids.filter((id) => !keep.has(id)).map((id) => deleteMotion(db, id)));
64
- return ids.filter((id) => !keep.has(id));
65
- }