cozyclay 2.1.0 → 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 (583) hide show
  1. package/CHANGELOG.md +65 -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 +22 -6
  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-BlT33CGf.js → first-shot-handoff-CC4xe4HM.js} +1 -1
  33. package/dist/assets/{landing-BSesrCpL.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/llms.txt +14 -0
  40. package/dist/sw.js +3 -6
  41. package/dist/workflow/index.html +5 -4
  42. package/mcp/LIVE-PROTOCOL.md +7 -47
  43. package/mcp/README.md +16 -26
  44. package/mcp/live-hub.mjs +6 -10
  45. package/mcp/package.json +1 -3
  46. package/mcp/tool-handlers.mjs +4 -168
  47. package/package.json +3 -22
  48. package/src/App.jsx +174 -2639
  49. package/src/agent-progress.js +79 -0
  50. package/src/agent-turn-mark.js +30 -0
  51. package/src/analytics.js +1 -19
  52. package/src/app-context.js +5 -0
  53. package/src/app-stage.jsx +2 -34
  54. package/src/asset-pane.jsx +6 -5
  55. package/src/camera-block.js +21 -0
  56. package/src/camera-direction.js +120 -0
  57. package/src/character-kind.js +3 -9
  58. package/src/command-bus.js +8 -6
  59. package/src/commands/cast.js +5 -34
  60. package/src/commands/elements/character.js +1 -1
  61. package/src/commands/elements/motion.js +1 -6
  62. package/src/commands/elements/object.js +6 -2
  63. package/src/commands/elements/shot.js +4 -1
  64. package/src/commands/elements.js +38 -14
  65. package/src/commands/index.js +3 -1
  66. package/src/commands/kits.js +37 -0
  67. package/src/commands/motion-timing.js +57 -0
  68. package/src/commands/motion.js +9 -152
  69. package/src/commands/motion3-compile.js +65 -0
  70. package/src/commands/motion3.js +207 -0
  71. package/src/commands/objects.js +6 -4
  72. package/src/commands/shot.js +49 -11
  73. package/src/commands/stage.js +14 -1
  74. package/src/commands/view.js +4 -1
  75. package/src/document-mirrors.js +42 -0
  76. package/src/document-store.js +4 -0
  77. package/src/domains/cast.js +19 -194
  78. package/src/domains/motion/motion-range-pin-rules.js +1 -1
  79. package/src/domains/motion/take-source.js +51 -0
  80. package/src/domains/motion.js +60 -1838
  81. package/src/domains/scenes.js +12 -55
  82. package/src/domains/shots.js +2 -2
  83. package/src/domains/stage.js +4 -2
  84. package/src/draft-ghost.js +46 -0
  85. package/src/draft-ghost.jsx +129 -0
  86. package/src/dualview.jsx +4 -3
  87. package/src/effects/EffectsLayer.jsx +112 -0
  88. package/src/effects/effect-meshes.js +232 -0
  89. package/src/effects/effect-record.js +103 -0
  90. package/src/effects/emitter.js +59 -0
  91. package/src/effects/simulate.js +261 -0
  92. package/src/effects/state.js +59 -0
  93. package/src/execution-telemetry.js +2 -3
  94. package/src/first-success-guide.jsx +5 -5
  95. package/src/ghost-set.js +257 -0
  96. package/src/held-object-edit.jsx +113 -0
  97. package/src/held-object-pose.js +175 -0
  98. package/src/hierarchy-model.js +1 -1
  99. package/src/hierarchy-panel.jsx +1 -1
  100. package/src/imported-mesh.jsx +103 -0
  101. package/src/kits/held.js +84 -0
  102. package/src/kits/index.js +70 -0
  103. package/src/kits/smoking.js +57 -0
  104. package/src/light-blockers.js +93 -0
  105. package/src/motion/beat-board-model.js +84 -0
  106. package/src/motion/beat-board.css +124 -0
  107. package/src/motion/beat-board.jsx +59 -0
  108. package/src/motion/center-of-mass.js +66 -0
  109. package/src/motion/fix-collisions.js +850 -0
  110. package/src/motion/physics-review.js +118 -0
  111. package/src/{ardy → motion}/physics-support.js +1 -76
  112. package/src/motion/rig-snapshot.js +22 -0
  113. package/src/motion/take-segments.jsx +140 -0
  114. package/src/{ardy → motion}/timeline-coordinates.js +0 -21
  115. package/src/{ardy → motion}/timeline.css +1 -2
  116. package/src/{ardy → motion}/timeline.jsx +48 -419
  117. package/src/motion/waypoints.js +76 -0
  118. package/src/motion3/beats.js +83 -0
  119. package/src/motion3/body.js +158 -0
  120. package/src/motion3/bones.js +47 -0
  121. package/src/motion3/carried.js +47 -0
  122. package/src/motion3/compile-job.js +75 -0
  123. package/src/motion3/compile-worker.js +10 -0
  124. package/src/motion3/compiler/anchors.js +105 -0
  125. package/src/motion3/compiler/arm-orient.js +128 -0
  126. package/src/motion3/compiler/channels.js +125 -0
  127. package/src/motion3/compiler/compile.js +205 -0
  128. package/src/motion3/compiler/gait-rhythm.js +97 -0
  129. package/src/motion3/compiler/grip.js +171 -0
  130. package/src/motion3/compiler/head-steady.js +48 -0
  131. package/src/motion3/compiler/hold.js +134 -0
  132. package/src/motion3/compiler/idle.js +113 -0
  133. package/src/motion3/compiler/limbs.js +170 -0
  134. package/src/motion3/compiler/math.js +72 -0
  135. package/src/motion3/compiler/pose.js +162 -0
  136. package/src/motion3/compiler/quat.js +49 -0
  137. package/src/motion3/compiler/skeleton.js +74 -0
  138. package/src/motion3/compiler/validate-common.js +32 -0
  139. package/src/motion3/compiler/validate-goals.js +118 -0
  140. package/src/motion3/compiler/validate-objects.js +143 -0
  141. package/src/motion3/compiler/validate-parts.js +116 -0
  142. package/src/motion3/compiler/validate.js +122 -0
  143. package/src/motion3/compiler/vocab.js +73 -0
  144. package/src/motion3/compiler/walk-pelvis.js +275 -0
  145. package/src/motion3/compiler/walk.js +207 -0
  146. package/src/motion3/compiler/wrist-clamp.js +32 -0
  147. package/src/motion3/delta.js +47 -0
  148. package/src/motion3/describe.js +174 -0
  149. package/src/motion3/docs.js +27 -0
  150. package/src/motion3/edit-carry.js +21 -0
  151. package/src/motion3/fix/ease.js +16 -0
  152. package/src/motion3/fix/ground.js +37 -0
  153. package/src/motion3/fix/index.js +52 -0
  154. package/src/motion3/fix/push.js +71 -0
  155. package/src/motion3/fix/reach.js +46 -0
  156. package/src/motion3/fix/retime.js +79 -0
  157. package/src/motion3/fix/self.js +211 -0
  158. package/src/motion3/fix/slide.js +70 -0
  159. package/src/motion3/fix/turn.js +51 -0
  160. package/src/motion3/follow.js +39 -0
  161. package/src/motion3/free-motion.js +173 -0
  162. package/src/motion3/grip-choice.js +130 -0
  163. package/src/motion3/hold-plan.js +157 -0
  164. package/src/motion3/json-patch.js +77 -0
  165. package/src/motion3/key-aim.js +105 -0
  166. package/src/motion3/key-edit.js +82 -0
  167. package/src/motion3/lenient.js +127 -0
  168. package/src/motion3/object-keys.js +236 -0
  169. package/src/motion3/object-pose.js +182 -0
  170. package/src/motion3/object-tracks.js +12 -0
  171. package/src/motion3/pipeline.js +102 -0
  172. package/src/motion3/playback.js +51 -0
  173. package/src/motion3/probes.js +91 -0
  174. package/src/motion3/qa/body-checks.js +34 -0
  175. package/src/motion3/qa/check-names.js +14 -0
  176. package/src/motion3/qa/checks.js +121 -0
  177. package/src/motion3/qa/contact-checks.js +51 -0
  178. package/src/motion3/qa/explain.js +80 -0
  179. package/src/motion3/qa/fmt.js +5 -0
  180. package/src/motion3/qa/foot-checks.js +72 -0
  181. package/src/motion3/qa/geometry.js +77 -0
  182. package/src/motion3/qa/goals.js +191 -0
  183. package/src/motion3/qa/ground.js +60 -0
  184. package/src/motion3/qa/hints-contact.js +179 -0
  185. package/src/motion3/qa/hints-goal.js +160 -0
  186. package/src/motion3/qa/hints-knee.js +24 -0
  187. package/src/motion3/qa/hints-motion.js +209 -0
  188. package/src/motion3/qa/hints-object.js +91 -0
  189. package/src/motion3/qa/index.js +42 -0
  190. package/src/motion3/qa/key-checks.js +82 -0
  191. package/src/motion3/qa/near.js +96 -0
  192. package/src/motion3/qa/object-checks.js +135 -0
  193. package/src/motion3/qa/patch.js +161 -0
  194. package/src/motion3/qa/severity.js +38 -0
  195. package/src/motion3/qa/spec-checks.js +65 -0
  196. package/src/motion3/qa/style.js +218 -0
  197. package/src/motion3/qa/wrist-checks.js +69 -0
  198. package/src/motion3/qa/wrist-obs.js +60 -0
  199. package/src/motion3/refs.js +120 -0
  200. package/src/motion3/repair-tune.js +121 -0
  201. package/src/motion3/repair.js +83 -0
  202. package/src/motion3/report.js +67 -0
  203. package/src/motion3/rigs/index.js +14 -0
  204. package/src/motion3/rigs/x-bot-tpose-body.json +1 -0
  205. package/src/motion3/rigs/x-bot-tpose.json +1 -0
  206. package/src/motion3/rigs/y-bot-tpose-body.json +1 -0
  207. package/src/motion3/rigs/y-bot-tpose.json +1 -0
  208. package/src/motion3/scene.js +60 -0
  209. package/src/motion3/schema/brief.js +89 -0
  210. package/src/motion3/schema/fields.js +44 -0
  211. package/src/motion3/schema/spec-examples.js +46 -0
  212. package/src/motion3/schema/topics.js +111 -0
  213. package/src/motion3/schema/types.js +147 -0
  214. package/src/motion3/search.js +117 -0
  215. package/src/motion3/second-hand.js +112 -0
  216. package/src/motion3/skills/basic.js +82 -0
  217. package/src/motion3/skills/book.js +76 -0
  218. package/src/motion3/skills/car.js +145 -0
  219. package/src/motion3/skills/frames-auto.js +29 -0
  220. package/src/motion3/skills/index.js +83 -0
  221. package/src/motion3/skills/info.js +65 -0
  222. package/src/motion3/skills/smoke-kit.js +109 -0
  223. package/src/motion3/skills/smoke.js +160 -0
  224. package/src/motion3/spec-store.js +59 -0
  225. package/src/motion3/take-edit.js +155 -0
  226. package/src/motion3/take.js +63 -0
  227. package/src/motion3/timeline.js +127 -0
  228. package/src/motion3/views.js +121 -0
  229. package/src/motion3/walk-plan.js +119 -0
  230. package/src/object-catalog.jsx +7 -1
  231. package/src/object-colors.js +98 -0
  232. package/src/object-gizmo-parts.js +115 -0
  233. package/src/object-gizmo.jsx +37 -122
  234. package/src/object-glow.js +139 -0
  235. package/src/object-hidden.js +33 -0
  236. package/src/object-look.js +196 -0
  237. package/src/object-size.js +10 -0
  238. package/src/object-travel.js +71 -6
  239. package/src/panels/CameraPanel.jsx +7 -7
  240. package/src/panels/HdrEnvironmentControls.jsx +59 -0
  241. package/src/panels/LightPanel.jsx +25 -4
  242. package/src/panels/ObjectLookPanel.jsx +88 -0
  243. package/src/panels/PoseImagePanel.jsx +1 -1
  244. package/src/panels/PosePanel.jsx +2 -17
  245. package/src/panels/RigControlPanel.jsx +5 -53
  246. package/src/panels/SubjectBox.jsx +4 -1
  247. package/src/panels/details.css +14 -0
  248. package/src/posestudio.jsx +4 -27
  249. package/src/project-browser.jsx +3 -4
  250. package/src/project-resources.js +18 -31
  251. package/src/project.js +13 -104
  252. package/src/props.jsx +25 -280
  253. package/src/rail-dot-drag.js +23 -0
  254. package/src/render-passes.js +10 -0
  255. package/src/resource-status.jsx +1 -0
  256. package/src/result-modal.jsx +4 -19
  257. package/src/room.jsx +20 -12
  258. package/src/scene-assets.js +8 -6
  259. package/src/scene-light-place.js +72 -0
  260. package/src/scene-lights.jsx +148 -0
  261. package/src/scene-objects.js +31 -139
  262. package/src/scenes.js +15 -105
  263. package/src/semantic-edit.js +2 -5
  264. package/src/set-pieces.jsx +183 -0
  265. package/src/settings-menu.jsx +3 -11
  266. package/src/shell/BottomDock.jsx +11 -53
  267. package/src/shell/CompositionGuideButton.jsx +20 -0
  268. package/src/shell/DetailsSlot.jsx +16 -200
  269. package/src/shell/GhostsMenuGroup.jsx +74 -0
  270. package/src/shell/LibrarySlot.jsx +1 -3
  271. package/src/shell/MenuBar.jsx +3 -3
  272. package/src/shell/PreferencesDialog.jsx +15 -264
  273. package/src/shell/PreferencesSlot.jsx +2 -8
  274. package/src/shell/ShotPreviewHeader.jsx +17 -0
  275. package/src/shell/TopBar.jsx +5 -83
  276. package/src/shell/ViewportToolbar.jsx +23 -93
  277. package/src/shell/agent-glass.css +19 -6
  278. package/src/shell/details-selection.js +42 -0
  279. package/src/shell/ghosts-menu.css +22 -0
  280. package/src/shell/glass-regions.css +29 -3
  281. package/src/shell/preferences.css +6 -1
  282. package/src/shell/viewport.css +12 -0
  283. package/src/shot-authoring.js +4 -7
  284. package/src/soft-shadows.js +37 -0
  285. package/src/stage-attach-frames.js +12 -12
  286. package/src/stage-canvas-rigs.jsx +3 -3
  287. package/src/stage-capture.jsx +42 -8
  288. package/src/stage-characters.jsx +22 -216
  289. package/src/stage-editor-gizmos.jsx +6 -6
  290. package/src/stage-environment.js +93 -0
  291. package/src/stage-hdr-builtins.js +107 -0
  292. package/src/stage-hdr-image.js +116 -0
  293. package/src/stage-hdr-library.js +70 -0
  294. package/src/stage-hdr-map.js +94 -0
  295. package/src/stage-hdr.js +130 -0
  296. package/src/stage-lighting.js +149 -0
  297. package/src/stage-occlusion.js +210 -0
  298. package/src/stage-sky.jsx +159 -0
  299. package/src/stage-startup-defaults.js +5 -9
  300. package/src/studio-actions.js +12 -14
  301. package/src/studio-agent-commands.js +46 -16
  302. package/src/studio-agent-motion.js +9 -9
  303. package/src/studio-agent-protocol.js +46 -73
  304. package/src/studio-app-binding.js +29 -45
  305. package/src/studio-asset-list.js +17 -0
  306. package/src/studio-contact-sheet.js +37 -5
  307. package/src/studio-elements.js +27 -7
  308. package/src/studio-entity-bounds.js +18 -0
  309. package/src/studio-error-codes.js +8 -0
  310. package/src/studio-export-framing.js +152 -0
  311. package/src/studio-lighting-check.js +103 -0
  312. package/src/studio-motion-visual.js +150 -0
  313. package/src/studio-schema-errors.js +80 -0
  314. package/src/studio-support-gap.js +74 -0
  315. package/src/styles.css +1 -19
  316. package/src/timeline-defaults.js +5 -0
  317. package/src/timeline-extent.js +8 -20
  318. package/src/tutorial-walk.js +17 -0
  319. package/src/ui-scrub.js +1 -1
  320. package/src/use-shot-camera-tracks.js +12 -0
  321. package/src/view-ghosts.js +97 -0
  322. package/src/view-ghosts.jsx +49 -0
  323. package/src/workflow/AgentPanel.jsx +47 -166
  324. package/src/workflow/AgentProgressCard.jsx +107 -0
  325. package/src/workflow/AgentReplyText.jsx +37 -0
  326. package/src/workflow/AgentTurnSteps.jsx +84 -0
  327. package/src/workflow/LoginPanel.jsx +140 -0
  328. package/src/workflow/WorkflowBuilder.jsx +7 -23
  329. package/src/workflow/agent-client.js +54 -51
  330. package/src/workflow/agent-panel.css +71 -1
  331. package/src/workflow/agent-progress-card.css +167 -0
  332. package/src/workflow/agent-progress-view.js +47 -0
  333. package/src/workflow/agent-turn-steps.css +113 -0
  334. package/src/workflow/agent-turn.js +191 -0
  335. package/src/workflow/login-client.js +34 -0
  336. package/src/workflow/motion-input.js +18 -31
  337. package/src/workflow/node-schema.js +1 -1
  338. package/src/workflow/reply-markdown.js +69 -0
  339. package/src/workflow/scene-asset-sync.js +8 -8
  340. package/tools/agent-session-stats.mjs +61 -0
  341. package/tools/dev-full.mjs +5 -56
  342. package/tools/eslint-baseline.json +12 -13
  343. package/tools/headless-studio.mjs +85 -0
  344. package/tools/knip-baseline.json +0 -179
  345. package/tools/loc-baseline.json +33 -83
  346. package/tools/motion3/extract-body.mjs +103 -0
  347. package/tools/motion3/extract-rig.mjs +85 -0
  348. package/tools/process-supervisor.mjs +0 -88
  349. package/tools/run-tests.mjs +9 -124
  350. package/bin/agent/motion-runtime.mjs +0 -76
  351. package/dist/ardy/cskel27-rest.json +0 -557
  352. package/dist/assets/analytics-B41FmPYI.js +0 -1
  353. package/dist/assets/app-Do-Og5Pu.css +0 -1
  354. package/dist/assets/app-DwP-tl2x.js +0 -4920
  355. package/dist/assets/config-Bx7pYsD9.js +0 -1
  356. package/dist/assets/config-DeoFTYzb.css +0 -1
  357. package/dist/assets/demo-Dt0qZfS7.js +0 -1
  358. package/dist/assets/scene-asset-sync-BCWDJB1d.js +0 -19
  359. package/dist/assets/scene-asset-sync-DjJ_cJ2R.css +0 -1
  360. package/dist/assets/ticket-jqMhqL88.js +0 -1
  361. package/dist/assets/workflow-Br-K7Srs.js +0 -179
  362. package/dist/demo/walk-then-stop.npz +0 -0
  363. package/mcp/ardy-prompts.mjs +0 -218
  364. package/src/ardy/auto-fix-panel.css +0 -108
  365. package/src/ardy/auto-physics.js +0 -1194
  366. package/src/ardy/client.js +0 -153
  367. package/src/ardy/convert.js +0 -313
  368. package/src/ardy/cskel27-neutral.js +0 -39
  369. package/src/ardy/cskel27.js +0 -77
  370. package/src/ardy/export.js +0 -165
  371. package/src/ardy/fix-collisions.js +0 -2257
  372. package/src/ardy/foot-lock.js +0 -230
  373. package/src/ardy/motion-calibration.js +0 -118
  374. package/src/ardy/motion-edit.js +0 -213
  375. package/src/ardy/motion-url.js +0 -55
  376. package/src/ardy/mp4-duration.js +0 -102
  377. package/src/ardy/npz.js +0 -595
  378. package/src/ardy/physics-panel.css +0 -21
  379. package/src/ardy/physics-panel.jsx +0 -89
  380. package/src/ardy/physics-refine.js +0 -111
  381. package/src/ardy/physics-review.js +0 -567
  382. package/src/ardy/physics-temporal.js +0 -54
  383. package/src/ardy/platform-fit-panel.css +0 -64
  384. package/src/ardy/platform-fit-panel.jsx +0 -41
  385. package/src/ardy/platform-fit.js +0 -326
  386. package/src/ardy/playback.js +0 -711
  387. package/src/ardy/pose-pin.js +0 -97
  388. package/src/ardy/prompt-clips.js +0 -30
  389. package/src/ardy/retime.js +0 -211
  390. package/src/ardy/root-drop.js +0 -369
  391. package/src/ardy/timeline-resize.js +0 -13
  392. package/src/ardy/to-cskel27.js +0 -225
  393. package/src/ardy/trim.js +0 -33
  394. package/src/ardy/waypoints.js +0 -369
  395. package/src/ardy-bridge-contract.js +0 -281
  396. package/src/domains/motion/motion-video-capture.js +0 -664
  397. package/src/i2v-motion-client.js +0 -77
  398. package/src/i2v-motion-studio.jsx +0 -180
  399. package/src/line-edit.js +0 -1348
  400. package/src/motion/generation.js +0 -105
  401. package/src/motion-readiness-ui.jsx +0 -67
  402. package/src/motion-readiness.js +0 -40
  403. package/src/motion-resources.js +0 -177
  404. package/src/motion-store.js +0 -65
  405. package/src/motion-trail.js +0 -601
  406. package/src/multimodel-ingest.js +0 -394
  407. package/src/panels/PromptBlocksPanel.jsx +0 -444
  408. package/src/panels/VideoCapturePanel.jsx +0 -181
  409. package/src/pose-extract/browser.js +0 -42
  410. package/src/pose-extract/detector.js +0 -100
  411. package/src/pose-extract/fit.js +0 -227
  412. package/src/pose-extract/image-frame.js +0 -51
  413. package/src/pose-extract/index.js +0 -58
  414. package/src/pose-extract/keyframes.js +0 -74
  415. package/src/pose-extract/landmarks.js +0 -162
  416. package/src/pose-extract/mirror.js +0 -153
  417. package/src/pose-extract/take.js +0 -131
  418. package/src/pose-extract/video-frames.js +0 -91
  419. package/src/take-recipe.js +0 -165
  420. package/src/trail-key-conflicts.js +0 -39
  421. package/src/trail-pick.js +0 -59
  422. package/src/use-i2v-capture-modal.js +0 -15
  423. package/tools/ardy/BRIDGE.md +0 -37
  424. package/tools/ardy/GVHMR-NPZ.md +0 -68
  425. package/tools/ardy/README.md +0 -124
  426. package/tools/ardy/__pycache__/cclay_gvhmr_worker.cpython-313.pyc +0 -0
  427. package/tools/ardy/artifacts.mjs +0 -34
  428. package/tools/ardy/bridge.mjs +0 -1732
  429. package/tools/ardy/bvh-cskel27.mjs +0 -1265
  430. package/tools/ardy/cclay_constrained_generate.py +0 -561
  431. package/tools/ardy/cclay_gvhmr_worker.py +0 -264
  432. package/tools/ardy/cclay_motion_edit.py +0 -510
  433. package/tools/ardy/cclay_sequence_generate.py +0 -705
  434. package/tools/ardy/cclay_worker.py +0 -304
  435. package/tools/ardy/dump-npz.py +0 -205
  436. package/tools/ardy/extract-rest.mjs +0 -299
  437. package/tools/ardy/extract.mjs +0 -507
  438. package/tools/ardy/footage.mjs +0 -467
  439. package/tools/ardy/gvhmr-floor.mjs +0 -102
  440. package/tools/ardy/gvhmr-to-cskel27.mjs +0 -21
  441. package/tools/ardy/gvhmr_fastpath.py +0 -168
  442. package/tools/ardy/gvhmr_trajectory.py +0 -240
  443. package/tools/ardy/merge-text-encoder.py +0 -160
  444. package/tools/ardy/mocap-metrics.mjs +0 -200
  445. package/tools/ardy/mocap-quality-gate.mjs +0 -110
  446. package/tools/ardy/motion-stabilize.mjs +0 -348
  447. package/tools/ardy/npz.mjs +0 -414
  448. package/tools/ardy/pose-to-npz.mjs +0 -106
  449. package/tools/ardy/prompt-limits.mjs +0 -5
  450. package/tools/ardy/run-edit-on-box.sh +0 -73
  451. package/tools/ardy/run-local.mjs +0 -233
  452. package/tools/ardy/run-on-box.sh +0 -593
  453. package/tools/ardy/run-sequence-on-box.sh +0 -177
  454. package/tools/ardy/runners/gvhmr-worker.mjs +0 -206
  455. package/tools/ardy/runners/index.mjs +0 -18
  456. package/tools/ardy/runners/local.mjs +0 -455
  457. package/tools/ardy/runners/proc.mjs +0 -125
  458. package/tools/ardy/runners/remote.mjs +0 -252
  459. package/tools/ardy/setup-local.mjs +0 -189
  460. package/tools/ardy/setup-text-encoder-on-box.sh +0 -93
  461. package/tools/ardy/setup-text-encoder.py +0 -306
  462. package/tools/ardy/smpl-cskel27.mjs +0 -178
  463. package/tools/ardy/visual-qa.mjs +0 -196
  464. package/tools/ardy/vq-car.mjs +0 -52
  465. package/tools/bench/EXP3.md +0 -61
  466. package/tools/bench/cclay_bench_extract_incam.py +0 -125
  467. package/tools/bench/cclay_bench_extract_obs.py +0 -204
  468. package/tools/bench/cclay_bench_runner.py +0 -42
  469. package/tools/bench/cube-contact.mjs +0 -162
  470. package/tools/bench/exp3.mjs +0 -152
  471. package/tools/bench/extract-bench-lib.mjs +0 -216
  472. package/tools/bench/extract-bench.mjs +0 -302
  473. package/tools/bench/fal-generate.mjs +0 -88
  474. package/tools/bench/fit/README.md +0 -189
  475. package/tools/bench/fit/camera.mjs +0 -59
  476. package/tools/bench/fit/contact.mjs +0 -419
  477. package/tools/bench/fit/footlock.mjs +0 -234
  478. package/tools/bench/fit/motion.mjs +0 -85
  479. package/tools/bench/fit/pin.mjs +0 -27
  480. package/tools/bench/fit/remote.mjs +0 -105
  481. package/tools/bench/fit-bench.mjs +0 -126
  482. package/tools/bench/fit-sanity.mjs +0 -45
  483. package/tools/bench/metrics.mjs +0 -313
  484. package/tools/bench/obs/depth.mjs +0 -258
  485. package/tools/bench/obs/extrinsics.mjs +0 -175
  486. package/tools/bench/obs/fit_mannequin_betas.py +0 -340
  487. package/tools/bench/obs/ground.mjs +0 -294
  488. package/tools/bench/obs/heading.mjs +0 -151
  489. package/tools/bench/obs/ladder.mjs +0 -555
  490. package/tools/bench/obs/mannequin-betas.json +0 -124
  491. package/tools/bench/obs/remote.mjs +0 -83
  492. package/tools/bench/obs/rest_joints.py +0 -52
  493. package/tools/bench/obs/ybot-targets.mjs +0 -49
  494. package/tools/bench/obs-bench.mjs +0 -520
  495. package/tools/bench/score.mjs +0 -343
  496. package/tools/bench/summarize.mjs +0 -86
  497. package/tools/demo-worker/README.md +0 -137
  498. package/tools/demo-worker/api-client.mjs +0 -233
  499. package/tools/demo-worker/generate.mjs +0 -301
  500. package/tools/demo-worker/index.mjs +0 -466
  501. package/tools/demo-worker/sign-core.mjs +0 -112
  502. package/tools/demo-worker/sign.mjs +0 -161
  503. package/tools/gt-render/browser.mjs +0 -159
  504. package/tools/gt-render/camera-math.mjs +0 -276
  505. package/tools/gt-render/page.mjs +0 -267
  506. package/tools/gt-render/render.mjs +0 -514
  507. package/tools/gt-render/scene-box.mjs +0 -31
  508. package/tools/gt-render/take-transform.mjs +0 -87
  509. package/tools/kimodo/compare-seam.mjs +0 -108
  510. package/tools/kimodo/constraints.mjs +0 -244
  511. package/tools/kimodo/edit.mjs +0 -259
  512. package/tools/kimodo/effector-constraints.mjs +0 -200
  513. package/tools/kimodo/generate.mjs +0 -646
  514. package/tools/kimodo/local-output.mjs +0 -265
  515. package/tools/kimodo/measure-pose.mjs +0 -65
  516. package/tools/kimodo/measure-preserve.mjs +0 -899
  517. package/tools/kimodo/measure-waypoints.mjs +0 -55
  518. package/tools/kimodo/mlx-generate.py +0 -111
  519. package/tools/kimodo/pose-constraints.mjs +0 -248
  520. package/tools/kimodo/preserve-mask.mjs +0 -543
  521. package/tools/kimodo/read-npz.mjs +0 -189
  522. package/tools/kimodo/run-edit-on-box.mjs +0 -155
  523. package/tools/kimodo/run-sequence-on-box.mjs +0 -178
  524. package/tools/kimodo/runner.mjs +0 -284
  525. package/tools/kimodo/seam-batch.mjs +0 -77
  526. package/tools/kimodo/setup-local.mjs +0 -30
  527. package/tools/kimodo/setup-on-box.sh +0 -339
  528. package/tools/kimodo/soma77-to-cskel27.mjs +0 -305
  529. package/tools/morphgs/assets/gen_truth.py +0 -39
  530. package/tools/morphgs/assets/mesh_ori_rig.txt +0 -27
  531. package/tools/morphgs/assets/playback-check.mjs +0 -30
  532. package/tools/morphgs/demo-gate.sh +0 -26
  533. package/tools/morphgs/fbx2morphgs.mjs +0 -68
  534. package/tools/morphgs/morphgs-to-cskel27.mjs +0 -105
  535. package/tools/morphgs/patches/preprocess_src-none-mode.patch +0 -76
  536. package/tools/morphgs/setup-on-cluster.sh +0 -126
  537. package/tools/projflow/driver.py +0 -1069
  538. package/tools/projflow/generate.mjs +0 -805
  539. package/tools/projflow/hml22-to-cskel27.mjs +0 -735
  540. package/tools/projflow/line-edit-job.mjs +0 -547
  541. package/tools/projflow/replay.mjs +0 -452
  542. package/tools/projflow/runner.mjs +0 -269
  543. package/tools/projflow/service.mjs +0 -742
  544. package/tools/track/DESIGN.md +0 -513
  545. package/tools/track/backfill-provenance.mjs +0 -116
  546. package/tools/track/budget.mjs +0 -8
  547. package/tools/track/check-rig.mjs +0 -209
  548. package/tools/track/diagnostics.schema.json +0 -60
  549. package/tools/track/export-rig.mjs +0 -256
  550. package/tools/track/fallback.mjs +0 -7
  551. package/tools/track/fk-parity-fixture.mjs +0 -161
  552. package/tools/track/gate.mjs +0 -349
  553. package/tools/track/masks.mjs +0 -318
  554. package/tools/track/metrics.mjs +0 -201
  555. package/tools/track/publish-obs.mjs +0 -108
  556. package/tools/track/py/check_env.py +0 -61
  557. package/tools/track/py/eval_lr.py +0 -229
  558. package/tools/track/py/lr_viterbi.py +0 -294
  559. package/tools/track/py/masks.py +0 -405
  560. package/tools/track/py/objective.py +0 -382
  561. package/tools/track/py/rig.py +0 -508
  562. package/tools/track/py/scene.py +0 -326
  563. package/tools/track/py/test_joint_indices.py +0 -123
  564. package/tools/track/py/test_lr_viterbi.py +0 -195
  565. package/tools/track/py/test_masks.py +0 -229
  566. package/tools/track/py/test_rig.py +0 -288
  567. package/tools/track/py/test_scene.py +0 -354
  568. package/tools/track/py/test_track.py +0 -328
  569. package/tools/track/py/track.py +0 -411
  570. package/tools/track/remote.mjs +0 -140
  571. package/tools/track/rig-dump.mjs +0 -227
  572. package/tools/track/run-box-tests.mjs +0 -14
  573. package/tools/track/run-box.mjs +0 -168
  574. package/tools/track/setup-box.sh +0 -59
  575. package/tools/track/study-2d.mjs +0 -728
  576. /package/src/{ardy → motion}/collision-blockers.js +0 -0
  577. /package/src/{ardy → motion}/ground.js +0 -0
  578. /package/src/{ardy → motion}/ik-drag.js +0 -0
  579. /package/src/{ardy → motion}/ik-key-json.js +0 -0
  580. /package/src/{ardy → motion}/ik.js +0 -0
  581. /package/src/{ardy → motion}/physics-surface.js +0 -0
  582. /package/src/{ardy → motion}/playback-clock.js +0 -0
  583. /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
- }