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,593 +0,0 @@
1
- #!/usr/bin/env bash
2
- # run-on-box.sh - generate an ARDY clip on the remote box.
3
- #
4
- # run-on-box.sh [<pose.npz>] --base <motion-id|npz-path> --prompt "<prompt>" \
5
- # --duration <seconds> [--dst-frame <N>] [--src-frame <N>] [--seed <S>] \
6
- # [--root-2d FRAME X Z HEADING ...] [--output <local.npz>] [--dry-run]
7
-
8
- #
9
- # The pose npz is an ARDY motion npz (it must carry local_rot_mats and
10
- # posed_joints; the frame at SRC_FRAME holds the pose). It is pushed to a
11
- # fresh remote temp dir and handed to the generator as
12
- # --pose-from <tmp>/pose.npz <src-frame> <dst-frame>
13
- # which pins the FULL-BODY pose onto <dst-frame> of the new clip. The base
14
- # motion supplies the unconstrained first pass (--base) and the generated
15
- # npz is pulled back. Nothing is ever written inside the ARDY checkout: the
16
- # base npz is read from ~/ardy/outputs/ (or outputs/omb/), the pose and the
17
- # result live under the remote temp dir, and the temp dir is removed on
18
- # exit.
19
- #
20
- # Three modes, chosen by what is given:
21
- # pose <pose.npz> present (the default): the pose npz is pushed to a
22
- # fresh remote temp dir and handed to the constrained generator
23
- # as --pose-from <tmp>/pose.npz <src-frame> <dst-frame>, pinning
24
- # the FULL-BODY pose onto <dst-frame> of the new clip. Requires
25
- # --base and --dst-frame.
26
- # waypoints no pose npz but one or more --root-2d groups: the constrained
27
- # generator runs WITHOUT --pose-from, so the clip follows the
28
- # path and prompt only, with no full-body pose constraint.
29
- # --dst-frame is ignored. --base is optional: given, it is the
30
- # unconstrained first pass; omitted, the script free-generates
31
- # the base clip itself first (two-pass mode: scripts/generate.py
32
- # with the same prompt/duration/seed under the remote temp dir,
33
- # then the constrained pass with --base pointing at that npz).
34
- # free no pose npz and no --root-2d: scripts/generate.py on the box,
35
- # the unconstrained generator (the constrained script refuses a
36
- # run with no target, pose or waypoint). --base and --dst-frame
37
- # are ignored.
38
- #
39
- # Each --root-2d group pins the root on the X/Z ground plane at one 0-based
40
- # clip frame (ARDY is Y-up; the horizontal plane is X and Z in meters, Y is
41
- # not constrained). X and Z must be within -20..20, HEADING is a yaw in
42
- # radians within -2π..2π or the literal 'none' to leave facing free. The
43
- # request carries 2..32 sparse authored keys starting at frame 0. Intermediate
44
- # root positions are left for the model to generate. Values are validated
45
- # locally and %q-quoted into the remote command like all other args.
46
- #
47
- # CPU is forced with CUDA_VISIBLE_DEVICES="" and that is deliberate: the
48
- # box's RTX 3070 has only 8.2 GB VRAM against 94 GB RAM, and nvidia-smi is
49
- # broken there (NVML driver/library version mismatch) even though
50
- # torch.cuda.is_available() still returns True - so without the override the
51
- # generator's `device = "cuda:0" if torch.cuda.is_available() else "cpu"`
52
- # line would pick a GPU that cannot hold the model. The preflight probe
53
- # below mirrors that exact line under the same env and prints the device the
54
- # generator will actually use.
55
- #
56
- # env (names shared with CozyClay scripts/ardy/sync-to-box):
57
- # CCLAY_ARDY_HOST ssh destination for the ARDY host (required)
58
- # CCLAY_ARDY_REPO ARDY checkout on the box (default $HOME/ardy)
59
- # CCLAY_ARDY_VENV venv python on the box (default ~/ardy/.venv-cuda/bin/python)
60
- set -euo pipefail
61
-
62
- HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
63
- HOST="${CCLAY_ARDY_HOST:-}"
64
- # REMOTE's default is escaped so the REMOTE shell expands $HOME (the same
65
- # trick sync-to-box uses); VENV_PY's default keeps a literal ~ so the REMOTE
66
- # shell tilde-expands it. Both resolve to the same ~/ardy on the box.
67
- REMOTE="${CCLAY_ARDY_REPO:-\$HOME/ardy}"
68
- # The box carries two venvs and they are NOT interchangeable: .venv runs the
69
- # CPU text-encoder service, .venv-cuda is what the motion generator was actually
70
- # run under (scripts/generate.py in generate_pc_monitor.log, 1060 MiB of VRAM).
71
- VENV_PY="${CCLAY_ARDY_VENV:-~/ardy/.venv-cuda/bin/python}"
72
- # Text encoder URL the generator falls back to when TEXT_ENCODER_URL is unset:
73
- # ardy/model/registry.py:47 DEFAULT_TEXT_ENCODER_URL. Generation dies without it,
74
- # so the preflight checks it rather than letting the run fail minutes in.
75
- ENCODER_URL="${CCLAY_ARDY_ENCODER_URL:-http://127.0.0.1:9550/}"
76
- # Force the motion model onto the CPU as well. Off by default: the encoder --
77
- # the part that does not fit beside the model in 8.2 GB -- already runs on the
78
- # CPU as its own service, and the model itself only needs about 1 GB.
79
- FORCE_CPU=0
80
-
81
- # BatchMode: never hang on a password prompt mid-pipeline. ConnectTimeout
82
- # fails fast on a dead host; ServerAlive* drops a wedged connection instead
83
- # of leaving the user staring at a silent ssh (30 s interval * 240 = 2 h).
84
- SSH_OPTS=(-o BatchMode=yes -o ConnectTimeout=10 -o ServerAliveInterval=30 -o ServerAliveCountMax=240)
85
- SCP_OPTS=(-o BatchMode=yes -o ConnectTimeout=10)
86
-
87
- usage() {
88
- cat >&2 <<'EOF'
89
- usage: run-on-box.sh [<pose.npz>] --base <motion-id|npz-path> --prompt "<prompt>" \
90
- --duration <seconds> [--dst-frame <N>] [--src-frame <N>] [--seed <S>] \
91
- [--root-2d FRAME X Z HEADING ...] [--output <local.npz>] [--dry-run]
92
-
93
-
94
- <pose.npz> OPTIONAL local ARDY motion npz (local_rot_mats +
95
- posed_joints) whose frame <src-frame> holds the CozyClay
96
- pose to honour. Omit it to generate without a full-body pose
97
- constraint: with --root-2d groups the clip is
98
- path/prompt-only constrained (with --base, or two-pass: the
99
- free generator produces the base clip first); without them
100
- it is free generation (scripts/generate.py on the box).
101
- --base base motion on the box: a bare id or bare <name>.npz
102
- (looked up in ~/ardy/outputs/<name>.npz, then
103
- ~/ardy/outputs/omb/<name>.npz), or a path containing /
104
- relative to the ARDY checkout / absolute. Required in pose
105
- mode; optional in waypoints mode (omitted = two-pass: the
106
- free generator first produces the base clip on the box);
107
- ignored in free mode.
108
- --prompt generation prompt for the constrained clip
109
- --duration clip duration in seconds (20 fps, 0.15..1200)
110
- --dst-frame clip frame to pin the pose onto, 0 <= dst < duration*20
111
- (pose mode only)
112
- --src-frame frame of <pose.npz> to copy (default 0; pose mode only)
113
- --seed optional seed for reproducible results
114
- --output local destination for the generated npz
115
- (default tools/ardy/out/<pose>-constrained.npz, or
116
- tools/ardy/out/<epoch>-generated.npz without a pose)
117
-
118
- --dry-run run the preflight checks, print the exact remote command,
119
- and exit without pushing, generating, or pulling
120
- --root-2d repeatable root waypoint: FRAME (0-based, inside the clip),
121
- X and Z in meters within -20..20 (ARDY is Y-up; the ground
122
- plane is X/Z, Y is not constrained), HEADING a yaw in
123
- radians within -2π..2π or the literal 'none' to leave
124
- facing free. Supply 2..32 sparse keys beginning at frame 0;
125
- ARDY generates all intermediate root motion.
126
-
127
- env:
128
- CCLAY_ARDY_HOST ssh destination for the ARDY host (required)
129
- CCLAY_ARDY_REPO ARDY checkout on the box (default $HOME/ardy)
130
- --cpu also run the motion model on the CPU (slower; the text encoder
131
- is on the CPU either way, as its own service)
132
-
133
- env:
134
- CCLAY_ARDY_VENV venv python on the box (default ~/ardy/.venv-cuda/bin/python)
135
- CCLAY_ARDY_ENCODER_URL text encoder service (default http://127.0.0.1:9550/)
136
- EOF
137
- exit 2
138
- }
139
-
140
- POSE_NPZ="" # legacy positional pose; normalized into POSE_ARGS after parsing
141
- POSE_ARGS=() # repeatable triples: LOCAL_NPZ SRC_FRAME DST_FRAME
142
- ROOT_MARGIN=""
143
- CONTACT_THRESHOLD=""
144
- HISTORY_FRAMES=""
145
- BASE=""
146
- PROMPT=""
147
- DURATION=""
148
- DST_FRAME=""
149
- SRC_FRAME=0
150
- SEED=""
151
- OUTPUT=""
152
- DRY_RUN=0
153
- ROOT_2D_ARGS=()
154
-
155
- while [[ $# -gt 0 ]]; do
156
- case "$1" in
157
- --base)
158
- [[ $# -ge 2 ]] || { echo "run-on-box: --base needs a motion id or npz path" >&2; usage; }
159
- BASE="$2"; shift 2 ;;
160
- --prompt)
161
- [[ $# -ge 2 ]] || { echo "run-on-box: --prompt needs text" >&2; usage; }
162
- PROMPT="$2"; shift 2 ;;
163
- --duration)
164
- [[ $# -ge 2 ]] || { echo "run-on-box: --duration needs seconds" >&2; usage; }
165
- [[ "$2" =~ ^[0-9]+([.][0-9]+)?$ ]] || { echo "run-on-box: --duration must be a number, got '$2'" >&2; usage; }
166
- DURATION="$2"; shift 2 ;;
167
- --dst-frame)
168
- [[ $# -ge 2 ]] || { echo "run-on-box: --dst-frame needs a frame number" >&2; usage; }
169
- [[ "$2" =~ ^[0-9]+$ ]] || { echo "run-on-box: --dst-frame must be a non-negative integer, got '$2'" >&2; usage; }
170
- DST_FRAME="$2"; shift 2 ;;
171
- --src-frame)
172
- [[ $# -ge 2 ]] || { echo "run-on-box: --src-frame needs a frame number" >&2; usage; }
173
- [[ "$2" =~ ^[0-9]+$ ]] || { echo "run-on-box: --src-frame must be a non-negative integer, got '$2'" >&2; usage; }
174
- SRC_FRAME="$2"; shift 2 ;;
175
- --seed)
176
- [[ $# -ge 2 ]] || { echo "run-on-box: --seed needs an integer" >&2; usage; }
177
- [[ "$2" =~ ^[0-9]+$ ]] || { echo "run-on-box: --seed must be a non-negative integer, got '$2'" >&2; usage; }
178
- SEED="$2"; shift 2 ;;
179
- --pose-from)
180
- [[ $# -ge 4 ]] || { echo "run-on-box: --pose-from needs LOCAL_NPZ SRC_FRAME DST_FRAME" >&2; usage; }
181
- [[ "$3" =~ ^[0-9]+$ && "$4" =~ ^[0-9]+$ ]] || { echo "run-on-box: --pose-from frames must be non-negative integers" >&2; usage; }
182
- POSE_ARGS+=("$2" "$3" "$4"); shift 4 ;;
183
- --root-2d)
184
- [[ $# -ge 5 ]] || { echo "run-on-box: --root-2d needs FRAME X Z HEADING" >&2; usage; }
185
- ROOT_2D_ARGS+=("$2" "$3" "$4" "$5"); shift 5 ;;
186
- --cpu)
187
- FORCE_CPU=1; shift ;;
188
- --root-margin)
189
- [[ $# -ge 2 && "$2" =~ ^[0-9]+([.][0-9]+)?$ ]] || { echo "run-on-box: --root-margin needs a non-negative number" >&2; usage; }
190
- ROOT_MARGIN="$2"; shift 2 ;;
191
- --history-frames)
192
- [[ $# -ge 2 && "$2" =~ ^[0-9]+$ ]] || { echo "run-on-box: --history-frames needs a non-negative integer" >&2; usage; }
193
- HISTORY_FRAMES="$2"; shift 2 ;;
194
- --contact-threshold)
195
- [[ $# -ge 2 && "$2" =~ ^[0-9]+([.][0-9]+)?$ ]] || { echo "run-on-box: --contact-threshold needs a non-negative number" >&2; usage; }
196
- CONTACT_THRESHOLD="$2"; shift 2 ;;
197
- --output)
198
- [[ $# -ge 2 ]] || { echo "run-on-box: --output needs a path" >&2; usage; }
199
- OUTPUT="$2"; shift 2 ;;
200
- --dry-run)
201
- DRY_RUN=1; shift ;;
202
- -h|--help)
203
- usage ;;
204
- -*)
205
- echo "run-on-box: unknown option '$1'" >&2; usage ;;
206
- *)
207
- [[ -z "$POSE_NPZ" ]] || { echo "run-on-box: one pose npz only, got '$1'" >&2; usage; }
208
- POSE_NPZ="$1"; shift ;;
209
- esac
210
- done
211
-
212
- [[ -n "$HOST" ]] || {
213
- echo "run-on-box: CCLAY_ARDY_HOST is required (for example: user@ardy-host)" >&2
214
- exit 2
215
- }
216
-
217
- # Normalize the legacy positional pose into the repeatable pose list.
218
- if [[ -n "$POSE_NPZ" ]]; then
219
- [[ -n "$DST_FRAME" ]] || usage
220
- POSE_ARGS+=("$POSE_NPZ" "$SRC_FRAME" "$DST_FRAME")
221
- fi
222
-
223
- # Mode by what is given: one or more pose npz files pin the full body,
224
- # waypoints alone constrain the path, and neither is free generation.
225
- MODE=""
226
- if [[ ${#POSE_ARGS[@]} -gt 0 ]]; then
227
- MODE="pose"
228
- elif [[ ${#ROOT_2D_ARGS[@]} -gt 0 ]]; then
229
- MODE="waypoints"
230
- else
231
- MODE="free"
232
- fi
233
- # Waypoints run the constrained generator: ONE model sampling call for the
234
- # whole clip. ARDY's trained window is 10 s — frames beyond it sit outside
235
- # the model's temporal horizon and the motion visibly degrades, so the last
236
- # line of defence refuses what every upstream layer should already have.
237
- if [[ "$MODE" == "waypoints" ]] && ! awk -v d="$DURATION" 'BEGIN { exit !(d <= 10) }'; then
238
- echo "run-on-box: --root-2d requires --duration <= 10 s (one-shot constrained generation; ARDY trained window), got ${DURATION}s" >&2
239
- exit 1
240
- fi
241
- # Waypoints without --base is two-pass: the free generator first produces
242
- # the base clip on the box (pass 1), then the constrained pass uses it.
243
- TWO_PASS=0
244
- [[ "$MODE" == "waypoints" && -z "$BASE" ]] && TWO_PASS=1
245
- IMPLICIT_POSE_BASE=0
246
-
247
- [[ -n "$PROMPT" && -n "$DURATION" ]] || usage
248
- if [[ "$MODE" == "pose" && -z "$BASE" ]]; then
249
- # Internal parser/reference baseline only. Full-body poses are the actual
250
- # constraints; users never select a semantic base motion.
251
- BASE="${CCLAY_ARDY_REFERENCE_BASE:-stand-upright-facing-forward-bring-both--0722140757}"
252
- IMPLICIT_POSE_BASE=1
253
- fi
254
- # --- local validation, mirroring the CozyClay wrapper's bounds -------------
255
- if [[ "$MODE" == "pose" ]]; then
256
- for ((i = 0; i < ${#POSE_ARGS[@]}; i += 3)); do
257
- pose_path="${POSE_ARGS[$i]}"
258
- [[ -f "$pose_path" ]] || { echo "run-on-box: pose npz not found: $pose_path" >&2; exit 1; }
259
- done
260
- fi
261
-
262
- [[ -n "$PROMPT" ]] || { echo "run-on-box: --prompt must not be empty" >&2; exit 1; }
263
- # The clip is int(duration * 20) frames at ARDY's 20 fps, capped at 1200 s
264
- # like the wrapper; the remote rejects anything below 3 frames because
265
- # inter-frame continuity is undefined there.
266
- awk -v d="$DURATION" 'BEGIN { exit !(d > 0 && d <= 1200) }' || {
267
- echo "run-on-box: --duration must be > 0 and <= 1200 seconds, got '$DURATION'" >&2; exit 1; }
268
- CLIP_FRAMES="$(awk -v d="$DURATION" 'BEGIN { printf "%d", d * 20 }')"
269
- [[ "$CLIP_FRAMES" -ge 3 ]] || {
270
- echo "run-on-box: --duration ${DURATION}s yields ${CLIP_FRAMES} frame(s) at 20 fps; a clip needs at least 3" >&2; exit 1; }
271
- if [[ "$MODE" == "pose" ]]; then
272
- for ((i = 0; i < ${#POSE_ARGS[@]}; i += 3)); do
273
- dst="${POSE_ARGS[$((i + 2))]}"
274
- awk -v f="$dst" -v m="$CLIP_FRAMES" 'BEGIN { exit !(f < m) }' || {
275
- echo "run-on-box: --pose-from destination $dst is outside 0..$((CLIP_FRAMES - 1)) (duration ${DURATION}s at 20 fps)" >&2; exit 1; }
276
- done
277
- fi
278
- # --- --root-2d validation: each group, then the set as a whole -------------
279
- # Mirrors the bridge contract: 2..32 sparse keys beginning at frame 0,
280
- # strictly ascending, X/Z in meters within -20..20, HEADING a finite number
281
- # of radians within -2π..2π or the literal 'none'.
282
- FLOAT_RE='^[+-]?([0-9]+([.][0-9]*)?|[.][0-9]+)([eE][+-]?[0-9]+)?$'
283
- validate_root_2d() {
284
- local n="${#ROOT_2D_ARGS[@]}" i frame x z heading prev=-1 count
285
- [[ $((n % 4)) -eq 0 ]] || { echo "run-on-box: internal error: --root-2d group list is misaligned" >&2; exit 1; }
286
- count=$((n / 4))
287
- if [[ "$count" -ne 0 && ( "$count" -lt 2 || "$count" -gt 32 ) ]]; then
288
- echo "run-on-box: --root-2d needs 2..32 sparse waypoints, got $count" >&2
289
- exit 1
290
- fi
291
- for ((i = 0; i < n; i += 4)); do
292
- frame="${ROOT_2D_ARGS[$i]}"
293
- x="${ROOT_2D_ARGS[$((i + 1))]}"
294
- z="${ROOT_2D_ARGS[$((i + 2))]}"
295
- heading="${ROOT_2D_ARGS[$((i + 3))]}"
296
- [[ "$frame" =~ ^[0-9]+$ ]] || { echo "run-on-box: --root-2d FRAME must be a non-negative integer, got '$frame'" >&2; exit 1; }
297
- awk -v f="$frame" -v m="$CLIP_FRAMES" 'BEGIN { exit !(f >= 0 && f < m) }' || {
298
- echo "run-on-box: --root-2d frame $frame is outside 0..$((CLIP_FRAMES - 1)) (duration ${DURATION}s at 20 fps)" >&2
299
- exit 1
300
- }
301
- awk -v f="$frame" -v p="$prev" 'BEGIN { exit !(f > p) }' || {
302
- echo "run-on-box: --root-2d frames must be strictly ascending; frame $frame repeats or precedes frame $prev" >&2
303
- exit 1
304
- }
305
- if [[ $i -eq 0 && "$frame" -ne 0 ]]; then
306
- echo "run-on-box: --root-2d start must be at frame 0, got frame $frame" >&2
307
- exit 1
308
- fi
309
- prev="$frame"
310
- [[ "$x" =~ $FLOAT_RE ]] || { echo "run-on-box: --root-2d X must be a number, got '$x'" >&2; exit 1; }
311
- awk -v v="$x" 'BEGIN { exit !(v >= -20 && v <= 20) }' || {
312
- echo "run-on-box: --root-2d X $x is outside -20..20 meters" >&2
313
- exit 1
314
- }
315
- [[ "$z" =~ $FLOAT_RE ]] || { echo "run-on-box: --root-2d Z must be a number, got '$z'" >&2; exit 1; }
316
- awk -v v="$z" 'BEGIN { exit !(v >= -20 && v <= 20) }' || {
317
- echo "run-on-box: --root-2d Z $z is outside -20..20 meters" >&2
318
- exit 1
319
- }
320
- if [[ "$heading" != "none" ]]; then
321
- [[ "$heading" =~ $FLOAT_RE ]] || {
322
- echo "run-on-box: --root-2d HEADING must be a number of radians or the literal 'none', got '$heading'" >&2
323
- exit 1
324
- }
325
- awk -v h="$heading" 'BEGIN { pi = atan2(0, -1); exit !(h >= -2*pi && h <= 2*pi) }' || {
326
- echo "run-on-box: --root-2d HEADING $heading rad is outside -2π..2π" >&2
327
- exit 1
328
- }
329
- fi
330
- done
331
- }
332
- validate_root_2d
333
- # SRC_FRAME indexes the pose npz; its real bound is only knowable on the
334
- # box, so like the reference wrapper we accept any non-negative integer here
335
- # and let the remote range-check it against the npz's frame count.
336
- if [[ "$MODE" == "pose" ]]; then
337
- FIRST_POSE="${POSE_ARGS[0]}"
338
- OUTPUT="${OUTPUT:-$HERE/out/$(basename "${FIRST_POSE%.npz}")-constrained.npz}"
339
- else
340
- OUTPUT="${OUTPUT:-$HERE/out/$(date +%s)-generated.npz}"
341
- fi
342
-
343
-
344
- # --- preflight: fail fast, cheapest first ---------------------------------
345
- if ! ssh "${SSH_OPTS[@]}" "$HOST" ":" </dev/null; then
346
- echo "run-on-box: cannot ssh to ${HOST} (BatchMode). Check network reachability and the SSH key agent." >&2
347
- exit 1
348
- fi
349
- echo "run-on-box: ssh to ${HOST} ok"
350
-
351
- # Free generation runs scripts/generate.py (the constrained script refuses a
352
- # run with no target, pose or waypoint); every other mode runs the
353
- # constrained generator. Two-pass waypoints mode needs BOTH scripts:
354
- # generate.py for the free base pass, then the constrained script.
355
- GEN_SCRIPT="scripts/cclay_constrained_generate.py"
356
- [[ "$MODE" == "free" ]] && GEN_SCRIPT="scripts/generate.py"
357
- GEN_CHECK="test -x ${VENV_PY} && test -f ${REMOTE}/${GEN_SCRIPT}"
358
- GEN_LABEL="${REMOTE}/${GEN_SCRIPT}"
359
- if [[ "$TWO_PASS" -eq 1 || "$IMPLICIT_POSE_BASE" -eq 1 ]]; then
360
- GEN_CHECK="test -x ${VENV_PY} && test -f ${REMOTE}/scripts/generate.py && test -f ${REMOTE}/scripts/cclay_constrained_generate.py"
361
- GEN_LABEL="${REMOTE}/scripts/generate.py and ${REMOTE}/scripts/cclay_constrained_generate.py"
362
- fi
363
- # shellcheck disable=SC2029 # REMOTE/VENV_PY expand remotely by design
364
- if ! ssh "${SSH_OPTS[@]}" "$HOST" "$GEN_CHECK" </dev/null; then
365
- echo "run-on-box: ${HOST} is missing ${VENV_PY} or ${GEN_LABEL}; sync the ARDY sources to the box first (CozyClay scripts/ardy/sync-to-box --apply)" >&2
366
- exit 1
367
- fi
368
-
369
- # The constrained generator is cclay-owned: the repo copy next to this
370
- # script is the source of truth, synced up on every run so the box can
371
- # never drift behind it (same pattern as run-sequence-on-box.sh).
372
- if ! ssh "${SSH_OPTS[@]}" "$HOST" "cat > ${REMOTE}/scripts/cclay_constrained_generate.py" \
373
- < "${HERE}/cclay_constrained_generate.py"; then
374
- echo "run-on-box: could not sync cclay_constrained_generate.py to ${HOST}" >&2
375
- exit 1
376
- fi
377
- echo "run-on-box: constrained generator synced to ${HOST}"
378
- echo "run-on-box: venv python and generator script present on ${HOST}"
379
-
380
-
381
- # Resolve --base on the box: anything containing a slash is a repo-relative
382
- # or absolute path and passes through; a bare id or bare <name>.npz is
383
- # looked up under outputs/ then outputs/omb/ (repo-relative, i.e.
384
- # ~/ardy/outputs*). Free mode takes no base, and waypoints mode without one
385
- # is two-pass: the pass-1 output is a fresh absolute temp path verified with
386
- # a plain test -f at the pass-1 step, never through this candidate lookup.
387
- if [[ -n "$BASE" ]]; then
388
- if [[ "$BASE" == */* ]]; then
389
- BASE_CANDIDATES="$(printf '%q' "$BASE")"
390
- BASE_LOOKED="'${BASE}'"
391
- else
392
- BASE_NAME="${BASE%.npz}"
393
- BASE_CANDIDATES="$(printf '%q' "outputs/${BASE_NAME}.npz") $(printf '%q' "outputs/omb/${BASE_NAME}.npz")"
394
- BASE_LOOKED="outputs/${BASE_NAME}.npz or outputs/omb/${BASE_NAME}.npz under ${REMOTE}"
395
- fi
396
- # shellcheck disable=SC2029 # REMOTE expands remotely by design
397
- if ! BASE_RESOLVED="$(ssh "${SSH_OPTS[@]}" "$HOST" "cd ${REMOTE} && for c in ${BASE_CANDIDATES}; do if test -f \"\$c\"; then printf '%s' \"\$c\"; exit 0; fi; done; exit 1" </dev/null)"; then
398
- echo "run-on-box: base motion '${BASE}' does not exist on ${HOST} (looked for ${BASE_LOOKED})" >&2
399
- exit 1
400
- fi
401
- echo "run-on-box: base motion on ${HOST}: ${BASE_RESOLVED}"
402
- fi
403
-
404
- # The hidden upright reference is only a convenient baseline when it is long
405
- # enough. A loaded clip can be longer than that fixture; in that case generate
406
- # an exact-duration base first instead of letting cclay_constrained_generate.py
407
- # fail after model load with "base has N frames but requested M".
408
- if [[ "$IMPLICIT_POSE_BASE" -eq 1 ]]; then
409
- # shellcheck disable=SC2029 # resolved path is selected from the box listing
410
- if ! BASE_FRAMES="$(ssh "${SSH_OPTS[@]}" "$HOST" \
411
- "cd ${REMOTE} && ${VENV_PY} -c 'import numpy as np,sys; print(np.load(sys.argv[1])[\"local_rot_mats\"].shape[0])' $(printf '%q' "$BASE_RESOLVED")" </dev/null)"; then
412
- echo "run-on-box: could not inspect implicit pose base ${BASE_RESOLVED}" >&2
413
- exit 1
414
- fi
415
- [[ "$BASE_FRAMES" =~ ^[0-9]+$ ]] || {
416
- echo "run-on-box: implicit pose base returned invalid frame count '${BASE_FRAMES}'" >&2
417
- exit 1
418
- }
419
- if [[ "$BASE_FRAMES" -lt "$CLIP_FRAMES" ]]; then
420
- echo "run-on-box: implicit pose base has ${BASE_FRAMES} frames; will free-generate ${CLIP_FRAMES}-frame base first"
421
- BASE_RESOLVED=""
422
- TWO_PASS=1
423
- fi
424
- fi
425
-
426
-
427
- # Probe the device the generator will use, with the SAME env it will run
428
- # under (CUDA_VISIBLE_DEVICES="" included). The one-liner mirrors the
429
- # generator's `device = "cuda:0" if torch.cuda.is_available() else "cpu"`
430
- # exactly, so what is printed is what the generation will pick.
431
- # shellcheck disable=SC2029 # REMOTE/VENV_PY expand remotely by design
432
- CUDA_ENV=""
433
- [[ "$FORCE_CPU" -eq 1 ]] && CUDA_ENV='CUDA_VISIBLE_DEVICES="" '
434
-
435
- # The text encoder is a separate CPU service; without it the generator loads the
436
- # model, then dies. Check it before spending minutes on the rest.
437
- if ! ENCODER_CODE="$(ssh "${SSH_OPTS[@]}" "$HOST" "curl -s -o /dev/null -w '%{http_code}' -m 10 ${ENCODER_URL}" </dev/null)"; then
438
- echo "run-on-box: could not reach ${HOST} to probe the text encoder" >&2
439
- exit 1
440
- fi
441
- if [[ "$ENCODER_CODE" != "200" ]]; then
442
- echo "run-on-box: text encoder at ${ENCODER_URL} answered '${ENCODER_CODE}', not 200." >&2
443
- echo "run-on-box: start it with: ssh ${HOST} 'cd ~/ardy && .venv/bin/python -u scripts/run_text_encoder_server.py --host 127.0.0.1 --port 9550 --device cpu'" >&2
444
- exit 1
445
- fi
446
- echo "run-on-box: text encoder ${ENCODER_URL} responding (cpu service)"
447
-
448
- if ! DEVICE="$(ssh "${SSH_OPTS[@]}" "$HOST" "cd ${REMOTE} && ${CUDA_ENV}${VENV_PY} -c 'import torch; print(\"cuda:0\" if torch.cuda.is_available() else \"cpu\")'" </dev/null)"; then
449
- echo "run-on-box: device probe on ${HOST} failed (venv python erroring?)" >&2
450
- exit 1
451
- fi
452
- [[ -n "$DEVICE" ]] || { echo "run-on-box: device probe on ${HOST} returned nothing" >&2; exit 1; }
453
- echo "run-on-box: generator will use device '${DEVICE}' on ${HOST}"
454
-
455
- # --- the generator invocation ---------------------------------------------
456
- # Args are shell-quoted with printf %q so prompts and paths survive the
457
- # remote shell. The output base is absolute (under the remote temp dir), so
458
- # the generator's _resolve_output_base honors it instead of placing it under
459
- # outputs/.
460
- # scripts/generate.py is the unconstrained generator: its prompt is
461
- # positional and it has no --base / --pose-from / --root-2d. Used for free
462
- # mode and for the two-pass base pass (pass 1); the npz lands at
463
- # <out_base>.npz.
464
- build_free_cmd() {
465
- local out_base="$1" cmd
466
- cmd="cd ${REMOTE} && ${CUDA_ENV}${VENV_PY} scripts/generate.py $(printf '%q' "$PROMPT")"
467
- cmd+=" --duration $(printf '%q' "$DURATION")"
468
- cmd+=" --output $(printf '%q' "$out_base")"
469
- if [[ -n "$SEED" ]]; then
470
- cmd+=" --seed $(printf '%q' "$SEED")"
471
- fi
472
- printf '%s' "$cmd"
473
- }
474
-
475
- build_remote_cmd() {
476
- local tmp_dir="$1" cmd i
477
- # Two-pass waypoints mode has no user base: BASE_RESOLVED stays empty and
478
- # pass 1's output npz (under the same remote temp dir, cleaned by the EXIT
479
- # trap) becomes the --base of the constrained pass.
480
- local base_arg="${BASE_RESOLVED:-${tmp_dir}/base.npz}"
481
- if [[ "$MODE" == "free" ]]; then
482
- cmd="$(build_free_cmd "${tmp_dir}/out")"
483
- else
484
- cmd="cd ${REMOTE} && ${CUDA_ENV}${VENV_PY} scripts/cclay_constrained_generate.py"
485
- cmd+=" --prompt $(printf '%q' "$PROMPT")"
486
- cmd+=" --duration $(printf '%q' "$DURATION")"
487
- cmd+=" --base $(printf '%q' "$base_arg")"
488
- if [[ "$MODE" == "pose" ]]; then
489
- for ((i = 0; i < ${#POSE_ARGS[@]}; i += 3)); do
490
- pose_index=$((i / 3))
491
- cmd+=" --pose-from $(printf '%q' "${tmp_dir}/pose-${pose_index}.npz")"
492
- cmd+=" $(printf '%q' "${POSE_ARGS[$((i + 1))]}") $(printf '%q' "${POSE_ARGS[$((i + 2))]}")"
493
- done
494
- fi
495
- # Each validated --root-2d group is %q-quoted value by value, so no
496
- # waypoint text can ever reach the remote shell unquoted.
497
- for ((i = 0; i < ${#ROOT_2D_ARGS[@]}; i += 4)); do
498
- cmd+=" --root-2d $(printf '%q' "${ROOT_2D_ARGS[$i]}")"
499
- cmd+=" $(printf '%q' "${ROOT_2D_ARGS[$((i + 1))]}")"
500
- cmd+=" $(printf '%q' "${ROOT_2D_ARGS[$((i + 2))]}")"
501
- cmd+=" $(printf '%q' "${ROOT_2D_ARGS[$((i + 3))]}")"
502
- done
503
- [[ -z "$ROOT_MARGIN" ]] || cmd+=" --root-margin $(printf '%q' "$ROOT_MARGIN")"
504
- [[ -z "$CONTACT_THRESHOLD" ]] || cmd+=" --contact-threshold $(printf '%q' "$CONTACT_THRESHOLD")"
505
- [[ -z "$HISTORY_FRAMES" ]] || cmd+=" --history_frames $(printf '%q' "$HISTORY_FRAMES")"
506
- cmd+=" --output $(printf '%q' "${tmp_dir}/out")"
507
- if [[ -n "$SEED" ]]; then
508
- cmd+=" --seed $(printf '%q' "$SEED")"
509
- fi
510
- fi
511
- printf '%s' "$cmd"
512
- }
513
-
514
-
515
- if [[ $DRY_RUN -eq 1 ]]; then
516
- REMOTE_TMP_PLACEHOLDER="<mktemp-dir-on-box>"
517
- MODE_LABEL="free generation (generate.py)"
518
- [[ "$MODE" == "pose" ]] && MODE_LABEL="full-body pose pin"
519
- [[ "$MODE" == "waypoints" ]] && MODE_LABEL="frame-0 root start + prompt (no pose)"
520
- [[ "$MODE" == "waypoints" && "$TWO_PASS" -eq 1 ]] && MODE_LABEL="frame-0 root start + prompt (no pose) - two-pass (free base first)"
521
- echo "run-on-box: DRY RUN - preflight done, nothing pushed, nothing generated, nothing pulled."
522
- echo "run-on-box: mode ${MODE} - ${MODE_LABEL}"
523
- [[ "$MODE" == "pose" ]] || echo "run-on-box: pose npz <none>"
524
- [[ "$MODE" != "pose" ]] || echo "run-on-box: pose pins $(( ${#POSE_ARGS[@]} / 3 ))"
525
- if [[ "$TWO_PASS" -eq 1 ]]; then
526
- echo "run-on-box: base motion pass-1 output ${REMOTE_TMP_PLACEHOLDER}/base.npz on ${HOST} (free-generated first)"
527
- else
528
- [[ -z "${BASE_RESOLVED:-}" ]] || echo "run-on-box: base motion ${BASE_RESOLVED} on ${HOST}"
529
- fi
530
- echo "run-on-box: device ${DEVICE}"
531
- echo "run-on-box: root-2d $(( ${#ROOT_2D_ARGS[@]} / 4 )) waypoint(s)"
532
- [[ "$MODE" != "pose" ]] || echo "run-on-box: would push $(( ${#POSE_ARGS[@]} / 3 )) pose npz file(s) to ${HOST}:${REMOTE_TMP_PLACEHOLDER}"
533
- if [[ "$TWO_PASS" -eq 1 ]]; then
534
- echo "run-on-box: pass 1/2 would run ssh ${HOST} \"$(build_free_cmd "${REMOTE_TMP_PLACEHOLDER}/base")\""
535
- echo "run-on-box: pass 2/2 would run ssh ${HOST} \"$(build_remote_cmd "$REMOTE_TMP_PLACEHOLDER")\""
536
- else
537
- echo "run-on-box: would run ssh ${HOST} \"$(build_remote_cmd "$REMOTE_TMP_PLACEHOLDER")\""
538
- fi
539
- echo "run-on-box: would pull scp -q -o BatchMode=yes ${HOST}:${REMOTE_TMP_PLACEHOLDER}/out.npz ${OUTPUT}"
540
- echo "run-on-box: would clean ssh ${HOST} \"rm -rf ${REMOTE_TMP_PLACEHOLDER}\""
541
- exit 0
542
- fi
543
-
544
- mkdir -p "$(dirname "$OUTPUT")"
545
-
546
- # Fresh temp dir on the box; the EXIT trap removes it even on failure, and
547
- # cleanup failure never masks the real result. Guard the path (must be
548
- # absolute, as mktemp guarantees) before it can reach the cleanup rm -rf.
549
- if ! REMOTE_TMP="$(ssh "${SSH_OPTS[@]}" "$HOST" "mktemp -d" </dev/null)"; then
550
- echo "run-on-box: could not create a temp dir on ${HOST} (mktemp failed)" >&2
551
- exit 1
552
- fi
553
- [[ "$REMOTE_TMP" == /* ]] || { echo "run-on-box: remote mktemp returned a non-absolute path '${REMOTE_TMP}'; aborting" >&2; exit 1; }
554
- trap 'ssh "${SSH_OPTS[@]}" "$HOST" "rm -rf ${REMOTE_TMP}" </dev/null >/dev/null 2>&1 || true' EXIT
555
-
556
- if [[ "$TWO_PASS" -eq 1 ]]; then
557
- # Pass 1: free-generate the base clip with the same prompt/duration/seed
558
- # the constrained pass will use. Its npz lives under REMOTE_TMP, so the
559
- # EXIT trap removes it with everything else.
560
- PASS1_CMD="$(build_free_cmd "${REMOTE_TMP}/base")"
561
- echo "run-on-box: pass 1/2: free-generating base clip on ${HOST} ..."
562
- # shellcheck disable=SC2029 # PASS1_CMD is a remote command by design
563
- ssh "${SSH_OPTS[@]}" "$HOST" "$PASS1_CMD" </dev/null
564
- BASE_RESOLVED="${REMOTE_TMP}/base.npz"
565
- # The pass-1 output is a fresh absolute temp path, not a listed base: no
566
- # BASE_CANDIDATES lookup, just verify the file appeared.
567
- # shellcheck disable=SC2029 # REMOTE_TMP expands remotely by design
568
- if ! ssh "${SSH_OPTS[@]}" "$HOST" "test -f ${BASE_RESOLVED}" </dev/null; then
569
- echo "run-on-box: pass 1/2 failed: ${BASE_RESOLVED} was not produced on ${HOST}" >&2
570
- exit 1
571
- fi
572
- echo "run-on-box: pass 1/2: base clip at ${HOST}:${BASE_RESOLVED}"
573
- fi
574
-
575
- if [[ "$MODE" == "pose" ]]; then
576
- for ((i = 0; i < ${#POSE_ARGS[@]}; i += 3)); do
577
- pose_index=$((i / 3))
578
- pose_path="${POSE_ARGS[$i]}"
579
- echo "run-on-box: pushing pose $((pose_index + 1))/$(( ${#POSE_ARGS[@]} / 3 )) to ${HOST}:${REMOTE_TMP}/pose-${pose_index}.npz ..."
580
- scp -q "${SCP_OPTS[@]}" "$pose_path" "$HOST:${REMOTE_TMP}/pose-${pose_index}.npz"
581
- done
582
- fi
583
-
584
- REMOTE_CMD="$(build_remote_cmd "$REMOTE_TMP")"
585
- echo "run-on-box: generating on ${HOST} ..."
586
- # shellcheck disable=SC2029 # REMOTE_CMD is a remote command by design
587
- ssh "${SSH_OPTS[@]}" "$HOST" "$REMOTE_CMD" </dev/null
588
-
589
- echo "run-on-box: pulling ${HOST}:${REMOTE_TMP}/out.npz -> ${OUTPUT} ..."
590
- # shellcheck disable=SC2029 # HOST:REMOTE_TMP is a remote path by design
591
- scp -q "${SCP_OPTS[@]}" "$HOST:${REMOTE_TMP}/out.npz" "$OUTPUT"
592
- [[ -s "$OUTPUT" ]] || { echo "run-on-box: pulled ${OUTPUT} is empty; the generation failed" >&2; exit 1; }
593
- echo "run-on-box: done - ${OUTPUT} ($(wc -c < "$OUTPUT" | tr -d ' ') bytes)"