cozyclay 1.6.0 → 1.7.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.
- package/CHANGELOG.md +151 -0
- package/README.md +62 -23
- package/bin/cozyclay.mjs +35 -22
- package/dist/app/index.html +3 -2
- package/dist/assets/app-BdCI2Bys.js +4851 -0
- package/dist/assets/app-CrB3UF4K.css +1 -0
- package/dist/assets/config-Bx7pYsD9.js +1 -0
- package/dist/assets/config-DeoFTYzb.css +1 -0
- package/dist/assets/demo-DEAy-8Eq.js +1 -0
- package/dist/assets/modulepreload-polyfill-P2Xu9kJm.js +1 -0
- package/dist/assets/ticket-DbrIKBrJ.js +1 -0
- package/dist/cozyclay-package.json +1 -1
- package/dist/index.html +8 -10
- package/dist/robots.txt +2 -0
- package/dist/sw.js +8 -2
- package/mcp/ardy-prompts.mjs +39 -9
- package/mcp/live-hub.mjs +10 -0
- package/mcp/package.json +1 -1
- package/mcp/runtime/package-lock.json +1 -1
- package/mcp/runtime/package.json +1 -1
- package/mcp/server.mjs +124 -22
- package/package.json +13 -7
- package/src/App.jsx +4699 -1969
- package/src/analytics.js +2 -0
- package/src/app-stage.jsx +2806 -0
- package/src/ardy/export.js +9 -1
- package/src/ardy/ik.js +238 -4
- package/src/ardy/motion-edit.js +41 -0
- package/src/ardy/motion-url.js +55 -0
- package/src/ardy/timeline.jsx +1019 -285
- package/src/asset-pane.jsx +18 -7
- package/src/auto-color.js +63 -0
- package/src/burn-in.js +91 -0
- package/src/camera-block.js +21 -4
- package/src/camera-follow.js +16 -6
- package/src/controls.jsx +44 -4
- package/src/dualview.jsx +89 -10
- package/src/error-boundary.jsx +41 -0
- package/src/gizmo-claim.js +48 -0
- package/src/grid-floor.jsx +97 -0
- package/src/grid-view.js +40 -0
- package/src/hierarchy-model.js +124 -71
- package/src/hierarchy-panel.jsx +101 -6
- package/src/line-edit.js +1348 -0
- package/src/live-control.js +11 -1
- package/src/main.jsx +4 -1
- package/src/matte.js +55 -0
- package/src/motion-trail.js +250 -0
- package/src/object-gizmo.jsx +57 -18
- package/src/object-path.js +224 -0
- package/src/planview.jsx +165 -14
- package/src/pose-thumbs.js +1 -1
- package/src/posestudio.jsx +99 -84
- package/src/project-browser.jsx +63 -6
- package/src/project.js +44 -0
- package/src/props.jsx +169 -27
- package/src/result-modal.jsx +2 -14
- package/src/room.jsx +37 -10
- package/src/scene-asset-cache.js +3 -0
- package/src/scene-objects.js +156 -3
- package/src/scenes.js +22 -1
- package/src/shot-authoring.js +9 -0
- package/src/shot-guides.js +90 -0
- package/src/shot.js +6 -7
- package/src/speed-envelope.js +254 -0
- package/src/styles.css +4831 -3436
- package/src/take-recipe.js +165 -0
- package/src/timeline-extent.js +39 -0
- package/src/use-render-activity.js +6 -0
- package/tools/ardy/BRIDGE.md +20 -316
- package/tools/ardy/README.md +22 -201
- package/tools/ardy/bridge.mjs +598 -73
- package/tools/ardy/bvh-cskel27.mjs +63 -5
- package/tools/ardy/prompt-limits.mjs +5 -0
- package/tools/ardy/run-on-box.sh +1 -1
- package/tools/ardy/runners/index.mjs +11 -16
- package/tools/ardy/visual-qa.mjs +38 -30
- package/tools/ardy/vq-car.mjs +1 -1
- package/tools/demo-worker/README.md +137 -0
- package/tools/demo-worker/api-client.mjs +239 -0
- package/tools/demo-worker/generate.mjs +303 -0
- package/tools/demo-worker/index.mjs +466 -0
- package/tools/demo-worker/sign-core.mjs +112 -0
- package/tools/demo-worker/sign.mjs +161 -0
- package/tools/dev-full.mjs +54 -36
- package/tools/kimodo/compare-seam.mjs +108 -0
- package/tools/kimodo/constraints.mjs +244 -0
- package/tools/kimodo/edit.mjs +264 -0
- package/tools/kimodo/effector-constraints.mjs +200 -0
- package/tools/kimodo/generate.mjs +597 -0
- package/tools/kimodo/measure-pose.mjs +65 -0
- package/tools/kimodo/measure-preserve.mjs +899 -0
- package/tools/kimodo/measure-waypoints.mjs +55 -0
- package/tools/kimodo/pose-constraints.mjs +250 -0
- package/tools/kimodo/preserve-mask.mjs +543 -0
- package/tools/kimodo/read-npz.mjs +189 -0
- package/tools/kimodo/run-edit-on-box.mjs +155 -0
- package/tools/kimodo/run-sequence-on-box.mjs +178 -0
- package/tools/kimodo/runner.mjs +246 -0
- package/tools/kimodo/seam-batch.mjs +77 -0
- package/tools/kimodo/setup-local.mjs +30 -0
- package/tools/kimodo/setup-on-box.sh +83 -0
- package/tools/kimodo/soma77-to-cskel27.mjs +305 -0
- package/tools/process-supervisor.mjs +1 -1
- package/tools/projflow/driver.py +1069 -0
- package/tools/projflow/generate.mjs +805 -0
- package/tools/projflow/hml22-to-cskel27.mjs +735 -0
- package/tools/projflow/line-edit-job.mjs +547 -0
- package/tools/projflow/replay.mjs +452 -0
- package/tools/projflow/runner.mjs +269 -0
- package/tools/projflow/service.mjs +742 -0
- package/tools/run-tests.mjs +108 -19
- package/dist/assets/app-CqOk49Bk.css +0 -1
- package/dist/assets/app-G55dPEDB.js +0 -4811
- package/src/generation/client.js +0 -56
- package/src/generation/generation-request.js +0 -17
- package/src/generation/session.js +0 -47
- package/src/generation/shot-spec.js +0 -40
- package/src/generation/use-generation.js +0 -44
- package/tools/generation/README.md +0 -29
- package/tools/generation/bridge.mjs +0 -162
- package/tools/generation/job-store.mjs +0 -51
- package/tools/generation/providers/kling.mjs +0 -76
- package/tools/generation/providers/runway.mjs +0 -101
- package/tools/generation/providers/seedance.mjs +0 -87
- package/tools/generation/providers/shared.mjs +0 -49
- package/tools/generation/providers/veo.mjs +0 -84
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,156 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 1.7.0
|
|
4
|
+
|
|
5
|
+
Kimodo becomes the default motion-generation backend, and a full edit-and-refine
|
|
6
|
+
loop lands on top of it: draw over a trail to reshape it, keep most of a take
|
|
7
|
+
and regenerate only a window, and step back through 20 checkpoints of a
|
|
8
|
+
take's history. Multi-character rigs, prop motion, and camera work all get
|
|
9
|
+
real editing surfaces, and three failure modes that used to blank the whole
|
|
10
|
+
studio now degrade with a way back instead.
|
|
11
|
+
|
|
12
|
+
### Kimodo motion backend, now default
|
|
13
|
+
|
|
14
|
+
- Kimodo, introduced behind `CCLAY_MOTION_BACKEND=kimodo`, is now the default
|
|
15
|
+
motion-generation backend.
|
|
16
|
+
- Root 2D path constraints, pose pinning (via Kimodo's fullbody constraints),
|
|
17
|
+
and motion-edit-by-regeneration (IK-adjust a span, then regenerate) now all
|
|
18
|
+
work on Kimodo — base clips still refuse, since Kimodo has no autoregressive
|
|
19
|
+
history input for them.
|
|
20
|
+
- The prompt-block cap is raised from 4s to 5s after measuring seam quality
|
|
21
|
+
across block lengths — 5s scored best short of a single seamless take, while
|
|
22
|
+
8s blocks collapsed and sub-2s blocks lost a third of their frames to the
|
|
23
|
+
transition window.
|
|
24
|
+
|
|
25
|
+
### Draw to edit a take (line editing)
|
|
26
|
+
|
|
27
|
+
- A new line-edit mode: drag a joint's motion trail to reshape it, or draw a
|
|
28
|
+
fresh stroke across empty space when the trail doesn't reach where you want.
|
|
29
|
+
Stroke endpoints snap to the take's own nearby frames so edits land where
|
|
30
|
+
you meant, and a smoothstep seam keeps the join from popping.
|
|
31
|
+
- Pin exact moments in 3D: scrub the playhead, drag a joint into place, and
|
|
32
|
+
pin up to 8 moments across a take.
|
|
33
|
+
- Runs on a new ProjFlow-based backend on the Kimodo box; edits are
|
|
34
|
+
seed-deterministic, so a release-quality regenerate reproduces the preview
|
|
35
|
+
bit for bit.
|
|
36
|
+
- Ctrl/Cmd+Z undoes the last pull; releasing a drag previews at reduced
|
|
37
|
+
quality first (~1s), then a full-quality generate confirms with the same
|
|
38
|
+
seed.
|
|
39
|
+
|
|
40
|
+
### Keep most of a take (preserve mode)
|
|
41
|
+
|
|
42
|
+
- A preserve slider and grouped per-limb masks let you regenerate only part of
|
|
43
|
+
a take — free one limb, or a masked region — instead of the whole clip.
|
|
44
|
+
- Effector constraints keep specific joints anchored through a regeneration.
|
|
45
|
+
|
|
46
|
+
### Take recipes and version history
|
|
47
|
+
|
|
48
|
+
- Every generate or edit now records its own recipe (seed, prompt blocks, and
|
|
49
|
+
edits in order), and extending a take re-sends its edits so refining before
|
|
50
|
+
extending no longer throws work away.
|
|
51
|
+
- A version strip above the timeline checkpoints every generate/edit up to 20
|
|
52
|
+
deep — click any entry to restore that take and its recipe.
|
|
53
|
+
- The edit surface collapses to two modes: 장면 (Kimodo blocking — new, again,
|
|
54
|
+
add-block) and 다듬기 (line-edit refinement, one click from a loaded take).
|
|
55
|
+
|
|
56
|
+
### Multi-character rigs and IK
|
|
57
|
+
|
|
58
|
+
- Every cast member now gets its own namespaced rig subtree in the Hierarchy
|
|
59
|
+
panel, not just the first — bones, IK badges, and focus all follow the
|
|
60
|
+
active character correctly (#76, #77, #78).
|
|
61
|
+
- IK corrections on an inactive character now survive a focus switch instead
|
|
62
|
+
of silently reverting to the uncorrected take (#77).
|
|
63
|
+
- Timeline pins (IK correction keys, prompt-clip ranges) migrate through
|
|
64
|
+
segment retimes instead of firing on the wrong frame after a slow-mo (#79).
|
|
65
|
+
- Body Contact: dragging a character down during IK editing now stops the
|
|
66
|
+
pelvis at the floor and holds any planted foot or reaching hand there,
|
|
67
|
+
measured per character from the actual skinned mesh — no more clay figures
|
|
68
|
+
hovering above the ground.
|
|
69
|
+
- Motion trails now render per body part in IK handle colors, with trail
|
|
70
|
+
editing and IK editing split into separate tools instead of one ambiguous
|
|
71
|
+
shared gizmo.
|
|
72
|
+
- A One-Euro filter now smooths SAM-extracted wrist tremor by speed —
|
|
73
|
+
quieting rest-state jitter while leaving fast strikes untouched (#84).
|
|
74
|
+
|
|
75
|
+
### Prop motion: travel paths and speed
|
|
76
|
+
|
|
77
|
+
- Scene objects (props) can now be given a travel path, authored the same way
|
|
78
|
+
as a camera rail: draw it in Top-View, refine it in the 3D scene, with a
|
|
79
|
+
mid-route point insertable by double-click.
|
|
80
|
+
- A speed graph — shared by props and the camera dolly — lets you drag a
|
|
81
|
+
stretch of the timeline faster or slower; the area under the curve is the
|
|
82
|
+
distance, so the rest of the segment compensates automatically, and a cut
|
|
83
|
+
pins the take at an exact time/distance point.
|
|
84
|
+
- Drag a prop onto a character or a specific rig bone in the Hierarchy to
|
|
85
|
+
attach it — it now rides that character's motion (a carried bat stays in a
|
|
86
|
+
walking character's hand instead of staying world-anchored).
|
|
87
|
+
|
|
88
|
+
### Viewport and lighting
|
|
89
|
+
|
|
90
|
+
- Auto Color mode: a topbar toggle stamps a stable, automatically derived
|
|
91
|
+
color onto every set object, the way Blender's random viewport shading
|
|
92
|
+
colors objects — display-only, so authored colors, saved projects, undo
|
|
93
|
+
history, and the MCP scene view are untouched.
|
|
94
|
+
- A new Grid view swaps the clay stage for a dark, Blender-style reference
|
|
95
|
+
grid for blocking work — overlay-only, so exports and the plan board never
|
|
96
|
+
see it.
|
|
97
|
+
- The key light is now a grabbable sun: move it like any other object, and
|
|
98
|
+
dial its warmth between cool daylight and warm sunset amber.
|
|
99
|
+
- Blender-style composition guides (thirds, golden ratio, center+diagonals,
|
|
100
|
+
safe areas) on the shot preview, and exported frames can burn in a slate +
|
|
101
|
+
zero-padded frame counter.
|
|
102
|
+
|
|
103
|
+
### Camera
|
|
104
|
+
|
|
105
|
+
- Crane marks get a 3-axis gizmo; a rail now always carries a crane profile,
|
|
106
|
+
and the old on/off toggle is gone (a flat profile at the follow height reads
|
|
107
|
+
as "off").
|
|
108
|
+
|
|
109
|
+
### Project workflow
|
|
110
|
+
|
|
111
|
+
- A startup chooser and local project session replace the always-on implicit
|
|
112
|
+
scene.
|
|
113
|
+
- New workflow-focused camera motion modes, plus several passes cleaning up
|
|
114
|
+
timeline control grouping, control visibility, and removing legacy
|
|
115
|
+
console/generation panels.
|
|
116
|
+
|
|
117
|
+
### AI control (MCP)
|
|
118
|
+
|
|
119
|
+
- `describe_scene` now reports the key light and the shot list; `update_object`
|
|
120
|
+
can set a prop's travel path; `load_motion` can target a specific character
|
|
121
|
+
by letter/slot/id and re-install an assembled take without a full
|
|
122
|
+
regeneration.
|
|
123
|
+
- Editors now identify themselves (project, scene, cast size) in
|
|
124
|
+
`live_status`, so multi-tab routing no longer depends on an opaque UUID.
|
|
125
|
+
|
|
126
|
+
### Hosted demo v1
|
|
127
|
+
|
|
128
|
+
- cozyclay.org's install-free demo got a real backend: a Cloudflare Worker
|
|
129
|
+
queue (Google OAuth + Turnstile, single-FIFO D1 queue, private R2 result
|
|
130
|
+
proxy) and an outbound-polling GPU box worker with zero inbound sockets.
|
|
131
|
+
|
|
132
|
+
### Reliability
|
|
133
|
+
|
|
134
|
+
- Three failure modes that used to blank the whole studio now degrade
|
|
135
|
+
instead: a render error anywhere in the app shows a message with a
|
|
136
|
+
reload-and-resume, a lost WebGL context (sleep/wake, driver restart) shows
|
|
137
|
+
an overlay and repaints on restore, and layout-save writes are guarded like
|
|
138
|
+
every other write in the app (#64).
|
|
139
|
+
- A selected object's own gizmo cage no longer blocks clicks meant for a
|
|
140
|
+
different object (#81).
|
|
141
|
+
- Recording no longer exports frozen tail frames after a cast member or
|
|
142
|
+
motion segment shrinks (#80).
|
|
143
|
+
|
|
144
|
+
### Also in this release
|
|
145
|
+
|
|
146
|
+
- Onboarding: a hosted-demo visitor now gets a "watch the sample" path instead
|
|
147
|
+
of landing on an empty stage, and three funnel-measurement gaps (startup
|
|
148
|
+
scene creation, video export, cutout import) are closed.
|
|
149
|
+
- The studio's PWA update banner finally has a listener, so a waiting version
|
|
150
|
+
reloads on request instead of running stale indefinitely.
|
|
151
|
+
- Objects can now be parented from the Inspector's own Parent picker, not
|
|
152
|
+
only through MCP.
|
|
153
|
+
|
|
3
154
|
## 1.6.0
|
|
4
155
|
|
|
5
156
|
The official npm package now records anonymous product usage so downloads can
|
package/README.md
CHANGED
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
<p align="center">
|
|
14
14
|
<a href="LICENSE"><img alt="License: AGPL-3.0" src="https://img.shields.io/badge/license-AGPL--3.0-blue"></a>
|
|
15
15
|
<a href="https://www.npmjs.com/package/cozyclay"><img alt="npm" src="https://img.shields.io/npm/v/cozyclay"></a>
|
|
16
|
-
<img alt="Node 22+" src="https://img.shields.io/badge/node-22%2B-brightgreen">
|
|
16
|
+
<img alt="Node 22.13+" src="https://img.shields.io/badge/node-22.13%2B-brightgreen">
|
|
17
17
|
<a href="https://github.com/NomaDamas/CozyClay/stargazers"><img alt="Stars" src="https://img.shields.io/github/stars/NomaDamas/CozyClay?style=flat"></a>
|
|
18
18
|
</p>
|
|
19
19
|
|
|
@@ -34,7 +34,7 @@ CozyClay is a browser-based 3D staging studio built with Three.js and React Thre
|
|
|
34
34
|
npx cozyclay
|
|
35
35
|
```
|
|
36
36
|
|
|
37
|
-
That is the whole install. **[cozyclay.org](https://cozyclay.org/)** has the demo reel and a walkthrough of what the studio does; to use it, run it on your own machine. It ships seeded with a pre-generated motion clip, so you can scrub the timeline, drive the cameras and draw a dolly rail straight away — generating *new* motion
|
|
37
|
+
That is the whole install. **[cozyclay.org](https://cozyclay.org/)** has the demo reel and a walkthrough of what the studio does; to use it, run it on your own machine. It ships seeded with a pre-generated motion clip, so you can scrub the timeline, drive the cameras and draw a dolly rail straight away — generating *new* motion is optional and uses the Kimodo bridge when configured.
|
|
38
38
|
|
|
39
39
|
## Demo
|
|
40
40
|
|
|
@@ -44,18 +44,18 @@ https://github.com/user-attachments/assets/1d0113e5-6922-443d-affc-1bdabc666247
|
|
|
44
44
|
|
|
45
45
|
| | |
|
|
46
46
|
| --- | --- |
|
|
47
|
-
| **Stage a scene** | Create primitives and set pieces, then move, rotate and scale them with a W/E/R gizmo. Grid snapping is a preference, not a law — hold `Ctrl` mid-drag to invert it. A bird's-eye plan view drives 2D root waypoints for character paths. |
|
|
47
|
+
| **Stage a scene** | Create primitives and set pieces, then move, rotate and scale them with a W/E/R gizmo. Grid snapping is a preference, not a law — hold `Ctrl` mid-drag to invert it. A bird's-eye plan view drives 2D root waypoints for character paths. The topbar's Auto Color toggle gives every object its own stable display color — Blender's random viewport color, so twenty grey blockout boxes stay tellable apart — without touching the colors you authored (captures include the display colors while it is on). |
|
|
48
48
|
| **Fly the camera** | Right-drag flies (WASD walks, Q/E cranes), middle-drag pans, Alt+drag orbits the selection, click selects, `F` frames — the muscle memory you already have from a 3D editor. |
|
|
49
49
|
| **Undo anything** | Every scene mutation goes through one history store: a drag, a scrub, an inspector edit is exactly one undo entry. `Esc` cancels an in-flight drag and restores the pre-drag transform. |
|
|
50
|
-
| **Generate motion** | Pose characters and export poses, sequence multi-phase motion as Prompt Blocks on a resizable timeline, send them to
|
|
50
|
+
| **Generate motion** | Pose characters and export poses, sequence multi-phase motion as Prompt Blocks on a resizable timeline, send them to Kimodo, then play the result back with sparse IK correction where the generated motion needs fixing. |
|
|
51
51
|
| **Direct it with an AI** | Connect Claude — or any MCP client — and ask for a shot in plain language. It places the cast, frames “a low wide profile”, generates multi-phase motion, and the viewport moves in front of you. See [AI control](#ai-control-mcp). |
|
|
52
52
|
|
|
53
53
|
## Requirements
|
|
54
54
|
|
|
55
|
-
- Node.js 22 or newer
|
|
55
|
+
- Node.js 22.13 or newer
|
|
56
56
|
- npm, or bun
|
|
57
57
|
- A Chromium-based browser
|
|
58
|
-
-
|
|
58
|
+
- An SSH-accessible NVIDIA machine running Kimodo, for motion generation — run `npm run kimodo:setup` once; the first setup downloads the Kimodo checkpoint and text-encoder stack.
|
|
59
59
|
|
|
60
60
|
## Quick start
|
|
61
61
|
|
|
@@ -65,17 +65,21 @@ npx cozyclay
|
|
|
65
65
|
bunx cozyclay
|
|
66
66
|
```
|
|
67
67
|
|
|
68
|
-
That downloads the built studio and opens it at `http://127.0.0.1:5180
|
|
68
|
+
That downloads the built studio and opens it at `http://127.0.0.1:5180/app/`. Nothing to compile, no dependency tree to install. Useful flags: `--port 5200`, `--no-open`, `--no-motion`.
|
|
69
69
|
|
|
70
70
|
A global install gives you `cclay`, the same command with less typing. Once a day the launcher checks npm for a newer release and prints a one-line notice after the studio is up; it stays quiet when you're current or offline. `cclay update` installs the latest release, and `--no-update-check` skips the check entirely.
|
|
71
71
|
|
|
72
|
-
Motion generation
|
|
72
|
+
Motion generation uses Kimodo by default once you point it at an SSH-accessible NVIDIA machine:
|
|
73
73
|
|
|
74
74
|
```bash
|
|
75
|
-
|
|
75
|
+
CCLAY_KIMODO_HOST=user@your-gpu-box npx cozyclay
|
|
76
76
|
```
|
|
77
77
|
|
|
78
|
-
|
|
78
|
+
Install the remote worker once:
|
|
79
|
+
|
|
80
|
+
```bash
|
|
81
|
+
CCLAY_KIMODO_HOST=user@your-gpu-box npm run kimodo:setup
|
|
82
|
+
```
|
|
79
83
|
|
|
80
84
|
## AI control (MCP)
|
|
81
85
|
|
|
@@ -116,20 +120,53 @@ npm install
|
|
|
116
120
|
npm run dev
|
|
117
121
|
```
|
|
118
122
|
|
|
119
|
-
Open `http://127.0.0.1:5180
|
|
123
|
+
Open `http://127.0.0.1:5180/app/`. `npm run dev` starts the studio together with its local Kimodo bridge once `CCLAY_KIMODO_HOST` points at a GPU box; without that variable it starts the studio alone and says so, and Block Generation stays unavailable until you set it. `npm run dev:ui` starts the browser UI alone in every case. The bridge listens on loopback only; Kimodo host variables are documented in [`tools/kimodo/setup-on-box.sh`](tools/kimodo/setup-on-box.sh).
|
|
124
|
+
|
|
125
|
+
## Hosted demo
|
|
126
|
+
|
|
127
|
+
Installing a GPU motion backend is the hard part, so `cozyclay.org` also runs a queued demo: a visitor writes one prompt, gets a ticket link, and a GPU box owned by the maintainer generates the motion and uploads it. The visitor never installs anything and never leaves the site — the result opens in the studio itself.
|
|
128
|
+
|
|
129
|
+
The pieces live in this repository, under `AGPL-3.0-or-later` like everything else:
|
|
130
|
+
|
|
131
|
+
| Path | Role |
|
|
132
|
+
| --- | --- |
|
|
133
|
+
| `demo/`, `d/` | Static composer and ticket/result pages, built into `dist/` by the same `npm run build` |
|
|
134
|
+
| `workers/api/` | Cloudflare Worker queue API (D1 for state, R2 for results), with its own pinned toolchain |
|
|
135
|
+
| `tools/demo-worker/` | The GPU-box poller. Outbound fetch only — it never opens a listening socket |
|
|
136
|
+
|
|
137
|
+
**Queue policy.** Jobs run in a single FIFO queue. All of these values live in `workers/api/src/policy.js`; nothing else carries a copy.
|
|
138
|
+
|
|
139
|
+
| Rule | Value |
|
|
140
|
+
| --- | --- |
|
|
141
|
+
| Active jobs per account | 1 |
|
|
142
|
+
| Daily cap | 2 per account |
|
|
143
|
+
| Global waiting cap | 200, then submissions are refused |
|
|
144
|
+
| Lease / heartbeat / hard timeout | 15 min lease, renewed every 60 s, 20 min hard stop |
|
|
145
|
+
| Attempts | 2 (one automatic retry); a failed job refunds the daily cap |
|
|
146
|
+
| Result retention | 30 days, then the R2 object is deleted |
|
|
147
|
+
| Prompt limit | shared with the studio via `tools/ardy/prompt-limits.mjs` |
|
|
120
148
|
|
|
121
|
-
|
|
122
|
-
|
|
149
|
+
**Secrets.** Never committed. Configure each with `wrangler secret put` against `workers/api/wrangler.toml`:
|
|
150
|
+
`GOOGLE_CLIENT_SECRET`, `CC_WORKER_SECRET`, `SESSION_SIGNING_KEY`, `TURNSTILE_SECRET_KEY`. The non-secret `GOOGLE_CLIENT_ID` and `TURNSTILE_SITE_KEY` vars in `wrangler.toml` must also be replaced before a real deployment.
|
|
123
151
|
|
|
124
|
-
|
|
152
|
+
**Running the API locally.**
|
|
125
153
|
|
|
126
154
|
```bash
|
|
127
|
-
|
|
155
|
+
npm run demo:api:install # npm --prefix workers/api ci
|
|
156
|
+
npm --prefix workers/api exec -- wrangler d1 migrations apply cozyclay-demo --local
|
|
157
|
+
npm run demo:api # wrangler dev on 127.0.0.1:8787
|
|
128
158
|
```
|
|
129
159
|
|
|
130
|
-
|
|
160
|
+
**Running the GPU-box worker.** The hosted queue worker has its own isolated runtime and reaches the API outbound only. It is independent from the local Studio's Kimodo backend.
|
|
161
|
+
|
|
162
|
+
```bash
|
|
163
|
+
CC_DEMO_API_BASE=https://api.cozyclay.org \
|
|
164
|
+
CC_WORKER_ID=box1 \
|
|
165
|
+
CC_WORKER_SECRET=... \
|
|
166
|
+
npm run demo:worker
|
|
167
|
+
```
|
|
131
168
|
|
|
132
|
-
|
|
169
|
+
See [`workers/api/README.md`](workers/api/README.md) for the deployment, migration and rollback runbook, and [`tools/demo-worker/README.md`](tools/demo-worker/README.md) for service units, environment-file permissions and the listening-socket check.
|
|
133
170
|
|
|
134
171
|
## Controls
|
|
135
172
|
|
|
@@ -138,16 +175,18 @@ See [`tools/ardy/README.md`](tools/ardy/README.md) for details. This workflow is
|
|
|
138
175
|
| Right-drag | Look around (fly) |
|
|
139
176
|
| RMB + WASD | Walk while flying |
|
|
140
177
|
| RMB + Q/E | Crane down / up |
|
|
178
|
+
| RMB + Shift | Boost fly speed 2.6× |
|
|
141
179
|
| Middle-drag | Pan |
|
|
142
180
|
| Alt + drag | Orbit the selection |
|
|
143
|
-
| Scroll | Dolly |
|
|
181
|
+
| Scroll | Dolly; while flying, sets the fly speed instead |
|
|
144
182
|
| Click | Select; empty space clears |
|
|
145
183
|
| W / E / R | Move / rotate / scale tool |
|
|
146
|
-
| Ctrl (during drag) | Invert grid snapping |
|
|
184
|
+
| Ctrl/Cmd (during drag) | Invert grid snapping |
|
|
147
185
|
| Ctrl/Cmd+Z, Ctrl/Cmd+Shift+Z | Undo / redo |
|
|
148
186
|
| Esc | Cancel the in-flight drag |
|
|
149
187
|
| End | Drop the selection to the surface |
|
|
150
188
|
| Ctrl/Cmd+D | Duplicate the selection |
|
|
189
|
+
| Delete / Backspace | Delete the selection |
|
|
151
190
|
| F | Frame the selection |
|
|
152
191
|
|
|
153
192
|
## Validate
|
|
@@ -160,15 +199,15 @@ See [`tools/ardy/README.md`](tools/ardy/README.md) for details. This workflow is
|
|
|
160
199
|
| `npm run test:objects` | Gizmo interaction in a real browser — needs `npm run dev:ui` in another shell |
|
|
161
200
|
| `npm run test:theme` / `test:appearance` / `test:layout` | UI theme, appearance, layout |
|
|
162
201
|
| `npm run test:lifecycle` | Dev-server process lifecycle |
|
|
163
|
-
| `npm run test:ardy` |
|
|
202
|
+
| `npm run test:ardy` | Motion conversion, playback, and IK pipeline |
|
|
164
203
|
| `cd mcp && npm install && npm run verify` | MCP server over real stdio — all 420 framing combinations |
|
|
165
204
|
| `cd mcp && npm run verify:live` | Live-control protocol against a fake editor (same `npm install` first) |
|
|
166
205
|
| `npm run build` | Production build |
|
|
167
206
|
|
|
168
|
-
Ad-hoc browser QA, while a dev server is available:
|
|
207
|
+
Ad-hoc browser QA, while a dev server is available (the browser opens the studio at `/app/`):
|
|
169
208
|
|
|
170
209
|
```bash
|
|
171
|
-
npm run qa:browser -- <qa-script>
|
|
210
|
+
npm run qa:browser -- node <qa-script>
|
|
172
211
|
```
|
|
173
212
|
|
|
174
213
|
## Contributing
|
|
@@ -229,4 +268,4 @@ PostHog's free plan retains events for 1 year.
|
|
|
229
268
|
|
|
230
269
|
GNU Affero General Public License v3.0 or later — see [`LICENSE`](LICENSE) and the transition details in [`LICENSING.md`](LICENSING.md). Modified network services must offer their users the corresponding source. Third-party projects retain their own licenses and copyright; see [`THIRD_PARTY_NOTICES.md`](THIRD_PARTY_NOTICES.md).
|
|
231
270
|
|
|
232
|
-
|
|
271
|
+
The hosted demo worker may use an externally installed [NVIDIA ARDY](https://github.com/nv-tlabs/ardy) runtime. ARDY is a separate third-party project owned and maintained by NVIDIA; it is not included in this repository, and CozyClay is not affiliated with or endorsed by NVIDIA. The local Studio uses Kimodo instead.
|
package/bin/cozyclay.mjs
CHANGED
|
@@ -3,12 +3,12 @@
|
|
|
3
3
|
* cozyclay - run the studio from a published package.
|
|
4
4
|
*
|
|
5
5
|
* `npx cozyclay` / `bunx cozyclay` should behave like `npm run dev` does in a
|
|
6
|
-
* clone: the studio in a browser, with the optional
|
|
6
|
+
* clone: the studio in a browser, with the optional motion sidecar wired up.
|
|
7
7
|
* The difference is that nothing is built here. The package ships the built
|
|
8
8
|
* `dist/`, so this launcher only has to
|
|
9
9
|
*
|
|
10
10
|
* - serve those files over loopback,
|
|
11
|
-
* - forward /ardy to its dynamically selected sidecar port (the job Vite's dev proxy does),
|
|
11
|
+
* - forward /ardy to its dynamically selected sidecar port (the same job Vite's dev proxy does),
|
|
12
12
|
* - keep the sidecar's lifetime tied to this process.
|
|
13
13
|
*
|
|
14
14
|
* It has no dependencies on purpose. A launcher that needs an install step
|
|
@@ -50,7 +50,12 @@ const OFFICIAL_PACKAGE = (
|
|
|
50
50
|
&& !existsSync(join(PKG_ROOT, ".git"))
|
|
51
51
|
&& verifyPackageMarker(join(DIST, "cozyclay-package.json"), PKG_ROOT, packageMetadata)
|
|
52
52
|
);
|
|
53
|
-
|
|
53
|
+
// A bridge is optional. Keep the endpoint unset until this launcher owns a
|
|
54
|
+
// sidecar; otherwise /ardy requests could accidentally reach an unrelated
|
|
55
|
+
// process that happens to be listening on the bridge's historical default
|
|
56
|
+
// port (5181).
|
|
57
|
+
let bridge = null;
|
|
58
|
+
let bridgePort = null;
|
|
54
59
|
|
|
55
60
|
const TYPES = {
|
|
56
61
|
".html": "text/html; charset=utf-8",
|
|
@@ -70,14 +75,14 @@ const TYPES = {
|
|
|
70
75
|
};
|
|
71
76
|
|
|
72
77
|
function parseArgs(argv) {
|
|
73
|
-
const opts = { port: 5180, host: "127.0.0.1",
|
|
78
|
+
const opts = { port: 5180, host: "127.0.0.1", motion: true, open: true, star: true, updateCheck: true };
|
|
74
79
|
for (let i = 0; i < argv.length; i += 1) {
|
|
75
80
|
const arg = argv[i];
|
|
76
81
|
if (arg === "--port" || arg === "-p") opts.port = Number(argv[++i]);
|
|
77
82
|
else if (arg.startsWith("--port=")) opts.port = Number(arg.slice(7));
|
|
78
83
|
else if (arg === "--host") opts.host = String(argv[++i]);
|
|
79
84
|
else if (arg.startsWith("--host=")) opts.host = arg.slice(7);
|
|
80
|
-
else if (arg === "--no-
|
|
85
|
+
else if (arg === "--no-motion") opts.motion = false;
|
|
81
86
|
else if (arg === "--no-open") opts.open = false;
|
|
82
87
|
else if (arg === "--no-star") opts.star = false;
|
|
83
88
|
else if (arg === "--no-update-check") opts.updateCheck = false;
|
|
@@ -115,7 +120,7 @@ const HELP = `cozyclay - browser-based 3D staging studio
|
|
|
115
120
|
npx cozyclay mcp run the MCP server (for Claude, Cursor, any MCP client)
|
|
116
121
|
cclay update install the latest cozyclay globally (npm install -g)
|
|
117
122
|
npx cozyclay --port 5200 serve on another port
|
|
118
|
-
npx cozyclay --no-
|
|
123
|
+
npx cozyclay --no-motion skip the optional motion-generation sidecar
|
|
119
124
|
npx cozyclay --no-open do not open a browser
|
|
120
125
|
npx cozyclay --no-star never ask about starring the repo
|
|
121
126
|
npx cozyclay --no-update-check
|
|
@@ -126,8 +131,8 @@ const HELP = `cozyclay - browser-based 3D staging studio
|
|
|
126
131
|
|
|
127
132
|
cclay is the same command, shorter: a global install gives you both.
|
|
128
133
|
|
|
129
|
-
Motion generation needs an SSH-reachable NVIDIA machine running
|
|
130
|
-
the sidecar at it with
|
|
134
|
+
Motion generation needs an SSH-reachable NVIDIA machine running Kimodo; point
|
|
135
|
+
the sidecar at it with CCLAY_KIMODO_HOST. Everything else - staging, posing,
|
|
131
136
|
paths, cameras, timeline, playback - runs locally with no extra setup.`;
|
|
132
137
|
|
|
133
138
|
function serveFile(res, path) {
|
|
@@ -145,6 +150,12 @@ function serveFile(res, path) {
|
|
|
145
150
|
// Forward /ardy to the sidecar. Same contract as the Vite dev proxy, so the
|
|
146
151
|
// browser code needs no build-time knowledge of how it was launched.
|
|
147
152
|
function proxyToBridge(req, res) {
|
|
153
|
+
if (!bridge || bridgePort === null || bridge.exitCode !== null || bridge.signalCode !== null) {
|
|
154
|
+
req.resume();
|
|
155
|
+
res.writeHead(503, { "content-type": "application/json; charset=utf-8" });
|
|
156
|
+
res.end(JSON.stringify({ error: "motion sidecar is not running" }));
|
|
157
|
+
return;
|
|
158
|
+
}
|
|
148
159
|
const upstream = httpRequest(
|
|
149
160
|
{ host: "127.0.0.1", port: bridgePort, path: req.url, method: req.method, headers: req.headers },
|
|
150
161
|
(upstreamRes) => {
|
|
@@ -156,7 +167,7 @@ function proxyToBridge(req, res) {
|
|
|
156
167
|
// An absent sidecar is an expected state, not a crash: the app treats a
|
|
157
168
|
// failed probe as "generation unavailable" and carries on.
|
|
158
169
|
res.writeHead(503, { "content-type": "application/json" });
|
|
159
|
-
res.end(JSON.stringify({ error: "
|
|
170
|
+
res.end(JSON.stringify({ error: "motion sidecar is not running" }));
|
|
160
171
|
});
|
|
161
172
|
req.pipe(upstream);
|
|
162
173
|
}
|
|
@@ -260,9 +271,9 @@ if (argv[0] === "mcp") {
|
|
|
260
271
|
} else {
|
|
261
272
|
|
|
262
273
|
const opts = parseArgs(argv);
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
274
|
+
if (opts.help) {
|
|
275
|
+
console.log(HELP);
|
|
276
|
+
process.exit(opts.invalid ? 1 : 0);
|
|
266
277
|
}
|
|
267
278
|
const version = readVersion();
|
|
268
279
|
if (opts.version) {
|
|
@@ -295,9 +306,8 @@ if (!existsSync(join(DIST, "app", "index.html"))) {
|
|
|
295
306
|
|
|
296
307
|
// The sidecar exits immediately without a box to talk to, so starting it
|
|
297
308
|
// unconditionally would greet a first-time `npx cozyclay` with an error it
|
|
298
|
-
// cannot act on. An unset
|
|
299
|
-
const
|
|
300
|
-
let bridge = null;
|
|
309
|
+
// cannot act on. An unset CCLAY_KIMODO_HOST is the normal case, not a fault.
|
|
310
|
+
const kimodoHost = process.env.CCLAY_KIMODO_HOST?.trim();
|
|
301
311
|
let server = null;
|
|
302
312
|
const startedAt = Date.now();
|
|
303
313
|
let shuttingDown = false;
|
|
@@ -316,7 +326,7 @@ async function shutdown(exitCode = 0) {
|
|
|
316
326
|
process.on("SIGINT", () => shutdown(130));
|
|
317
327
|
process.on("SIGTERM", () => shutdown(143));
|
|
318
328
|
|
|
319
|
-
if (opts.
|
|
329
|
+
if (opts.motion && kimodoHost && existsSync(BRIDGE)) {
|
|
320
330
|
try {
|
|
321
331
|
({ child: bridge, port: bridgePort } = await startBridge({
|
|
322
332
|
command: process.execPath,
|
|
@@ -342,7 +352,7 @@ if (opts.ardy && ardyHost && existsSync(BRIDGE)) {
|
|
|
342
352
|
}));
|
|
343
353
|
} catch (err) {
|
|
344
354
|
console.error(`cozyclay: motion generation sidecar failed: ${err.message}`);
|
|
345
|
-
console.error("cozyclay: studio did not start; set COZYCLAY_BRIDGE_PORT to an available port or use --no-
|
|
355
|
+
console.error("cozyclay: studio did not start; set COZYCLAY_BRIDGE_PORT to an available port or use --no-motion.");
|
|
346
356
|
if (bridge) await terminateOwned(bridge);
|
|
347
357
|
process.exit(1);
|
|
348
358
|
}
|
|
@@ -432,7 +442,10 @@ server = createServer((req, res) => {
|
|
|
432
442
|
if (runtime.firstLaunch) {
|
|
433
443
|
markTelemetryFirstLaunch(STATE_FILE);
|
|
434
444
|
}
|
|
435
|
-
|
|
445
|
+
// __COZYCLAY_LIVE__ opts the production build into the loopback live
|
|
446
|
+
// socket (src/App.jsx gates on DEV || this flag); without it an
|
|
447
|
+
// npx-served studio can never attach to the MCP server's live hub.
|
|
448
|
+
const script = `<script>window.__COZYCLAY_RUNTIME__ = ${JSON.stringify(runtime).replaceAll("<", "\\u003c")}; window.__COZYCLAY_LIVE__ = true;</script>`;
|
|
436
449
|
const html = readFileSync(target, "utf8").replace("</head>", `${script}\n</head>`);
|
|
437
450
|
res.writeHead(200, {
|
|
438
451
|
"content-type": "text/html; charset=utf-8",
|
|
@@ -461,12 +474,12 @@ server.listen({ port: opts.port, host: "127.0.0.1", ipv6Only: false }, () => {
|
|
|
461
474
|
const url = `http://127.0.0.1:${opts.port}/app/`;
|
|
462
475
|
console.log(`CozyClay is running at ${url}`);
|
|
463
476
|
console.log("Use a Chromium-based browser — Safari and Firefox are not supported.");
|
|
464
|
-
if (!opts.
|
|
465
|
-
else if (bridge) console.log(`Motion generation: sidecar running against ${
|
|
477
|
+
if (!opts.motion) console.log("Motion generation: off (--no-motion).");
|
|
478
|
+
else if (bridge) console.log(`Motion generation: sidecar running against ${kimodoHost}.`);
|
|
466
479
|
else
|
|
467
480
|
console.log(
|
|
468
|
-
"Motion generation: off. It runs on an SSH-reachable NVIDIA machine with
|
|
469
|
-
"set
|
|
481
|
+
"Motion generation: off. It runs on an SSH-reachable NVIDIA machine with Kimodo;\n" +
|
|
482
|
+
"set CCLAY_KIMODO_HOST=user@host to turn it on. Everything else works without it.",
|
|
470
483
|
);
|
|
471
484
|
if (opts.open) openBrowser(url);
|
|
472
485
|
void updatePending?.then((latest) => {
|
package/dist/app/index.html
CHANGED
|
@@ -34,8 +34,9 @@
|
|
|
34
34
|
<title>CozyClay Studio — 3D staging and previs in your browser</title>
|
|
35
35
|
|
|
36
36
|
<script defer src="https://static.cloudflareinsights.com/beacon.min.js" data-cf-beacon='{"token": "6a4384d1375d4ffc8a30a6346a8ad1a0"}'></script>
|
|
37
|
-
<script type="module" crossorigin src="/assets/app-
|
|
38
|
-
<link rel="
|
|
37
|
+
<script type="module" crossorigin src="/assets/app-BdCI2Bys.js"></script>
|
|
38
|
+
<link rel="modulepreload" crossorigin href="/assets/modulepreload-polyfill-P2Xu9kJm.js">
|
|
39
|
+
<link rel="stylesheet" crossorigin href="/assets/app-CrB3UF4K.css">
|
|
39
40
|
</head>
|
|
40
41
|
<body>
|
|
41
42
|
<div id="root">
|