cozyclay 2.1.1 → 3.0.1

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