cozyclay 3.1.3 → 3.1.5
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 +98 -0
- package/INSTALL_FOR_AGENTS.md +3 -0
- package/README.md +5 -0
- package/bin/agent/agent-failure-codes.mjs +31 -0
- package/bin/agent/agent-routes.mjs +5 -10
- package/bin/agent/agent-runner.mjs +5 -3
- package/bin/cozyclay.mjs +18 -112
- package/bin/star-prompt.mjs +123 -0
- package/bin/studio-routes.mjs +69 -0
- package/bin/update-check.mjs +53 -5
- package/bin/update-install.mjs +136 -0
- package/bin/update-routes.mjs +80 -0
- package/dist/ai-camera-control/index.html +1 -1
- package/dist/app/index.html +5 -6
- package/dist/assets/{TaskBoard-U1Yl-4F1.js → TaskBoard-DwXhsJXz.js} +1 -1
- package/dist/assets/app-CEP7jzXB.css +1 -0
- package/dist/assets/app-CTKcyPM-.js +5144 -0
- package/dist/assets/compile-worker-or7djI2v.js +6 -0
- package/dist/assets/{motion3-compile-DDG3wklD.js → motion3-compile-Du7UXrWn.js} +1 -1
- package/dist/assets/scene-asset-sync-t45pWCyq.js +13 -0
- package/dist/assets/task-board-CkLZGPkv.js +15 -0
- package/dist/assets/workflow-DJwOUzqJ.js +179 -0
- package/dist/cozyclay-package.json +1 -1
- package/dist/greybox-to-video/index.html +3 -3
- package/dist/index.html +533 -264
- package/dist/media/cozyclay-mark.png +0 -0
- package/dist/media/cozyclay-truck-demo-poster.jpg +0 -0
- package/dist/media/step-move-poster.jpg +0 -0
- package/dist/media/step-shoot-poster.jpg +0 -0
- package/dist/media/step-stage-poster.jpg +0 -0
- package/dist/media/truck-greybox-poster.jpg +0 -0
- package/dist/media/truck-seedance-poster.jpg +0 -0
- package/dist/previs-software/index.html +4 -4
- package/dist/privacy/index.html +46 -19
- package/dist/seedance-camera-control/index.html +3 -3
- package/dist/sitemap.xml +6 -6
- package/dist/workflow/index.html +3 -4
- package/package.json +1 -1
- package/src/App.jsx +59 -70
- package/src/analytics-controls.js +17 -0
- package/src/analytics-friction.js +124 -0
- package/src/analytics-strict.js +139 -0
- package/src/analytics-values.js +49 -0
- package/src/analytics.js +37 -133
- package/src/asset-pane.jsx +4 -5
- package/src/click-friction.js +101 -0
- package/src/command-bus-telemetry.js +155 -0
- package/src/command-bus.js +10 -8
- package/src/commands/motion3.js +7 -2
- package/src/controls.jsx +3 -2
- package/src/crash-telemetry.js +45 -0
- package/src/domains/cast.js +2 -11
- package/src/domains/motion.js +49 -38
- package/src/domains/objects.js +3 -0
- package/src/domains/scene-undo.js +27 -0
- package/src/domains/scenes.js +1 -1
- package/src/error-boundary.jsx +2 -0
- package/src/export-attempt.js +100 -0
- package/src/export-workspace.js +12 -1
- package/src/first-success-guide.jsx +11 -7
- package/src/guide-telemetry.js +60 -0
- package/src/hierarchy-panel.jsx +2 -1
- package/src/live-control.js +2 -9
- package/src/locale-zh/domains.js +1 -0
- package/src/main.jsx +5 -0
- package/src/motion/timeline.jsx +9 -5
- package/src/motion-draft-telemetry.js +62 -0
- package/src/motion3/compiler/channels.js +7 -5
- package/src/motion3/compiler/compile.js +6 -2
- package/src/motion3/compiler/finger-close.js +84 -7
- package/src/motion3/compiler/head-lead.js +70 -0
- package/src/motion3/compiler/head-steady.js +1 -1
- package/src/motion3/compiler/limbs.js +10 -3
- package/src/motion3/compiler/pose.js +1 -1
- package/src/motion3/compiler/validate.js +5 -3
- package/src/motion3/pipeline.js +4 -0
- package/src/motion3/qa/explain.js +2 -1
- package/src/motion3/qa/finger-checks.js +80 -0
- package/src/motion3/qa/gait-observe.js +88 -0
- package/src/motion3/qa/grasp-checks.js +3 -0
- package/src/motion3/qa/hints-grasp.js +8 -0
- package/src/motion3/qa/index.js +2 -1
- package/src/motion3/qa/motion-life.js +26 -3
- package/src/motion3/schema/topics.js +1 -1
- package/src/playback-telemetry.js +107 -0
- package/src/post-turn-edits.js +62 -0
- package/src/search-friction.js +49 -0
- package/src/semantic-edit.js +19 -6
- package/src/shell/BoardSlot.jsx +3 -3
- package/src/shell/BottomDock.jsx +1 -1
- package/src/shell/DetailsSlot.jsx +1 -1
- package/src/shell/MenuBar.jsx +13 -13
- package/src/shell/StudioShell.jsx +7 -1
- package/src/shell/TopBar.jsx +2 -0
- package/src/shell/ViewportToolbar.jsx +2 -2
- package/src/shortcut-friction.js +122 -0
- package/src/stage-canvas-rigs.jsx +3 -4
- package/src/studio-telemetry.js +97 -0
- package/src/thrash-friction.js +90 -0
- package/src/update/UpdateChip.jsx +86 -0
- package/src/update/UpdateDialog.jsx +147 -0
- package/src/update/update-client.js +188 -0
- package/src/update/update-telemetry.js +78 -0
- package/src/update/update.css +94 -0
- package/src/use-webgl-available.js +35 -0
- package/src/workflow/AgentPanel.jsx +29 -30
- package/src/workflow/CozySceneNode.jsx +1 -1
- package/src/workflow/LoginPanel.jsx +15 -5
- package/src/workflow/agent-client.js +10 -91
- package/src/workflow/agent-failure-codes.js +47 -0
- package/src/workflow/agent-turn-telemetry.js +222 -0
- package/src/workflow/connect-telemetry.js +86 -0
- package/src/workflow/prompt-telemetry.js +105 -0
- package/tools/dev/pages/privacy.html +44 -17
- package/tools/loc-baseline.json +10 -10
- package/tools/run-tests.mjs +8 -8
- package/dist/assets/app-BdRlMf4J.css +0 -1
- package/dist/assets/app-DwETmoQZ.js +0 -5144
- package/dist/assets/compile-worker-oy_P9HHh.js +0 -6
- package/dist/assets/landing-y3EqDgSZ.js +0 -1
- package/dist/assets/modulepreload-polyfill-P2Xu9kJm.js +0 -1
- package/dist/assets/scene-asset-sync-Bz59Ghdz.js +0 -13
- package/dist/assets/task-board-DFfNa0xg.js +0 -15
- package/dist/assets/workflow-BMJGd_gZ.js +0 -179
- package/dist/media/playground-poster.jpg +0 -0
package/bin/update-check.mjs
CHANGED
|
@@ -52,20 +52,32 @@ export function compareVersions(a, b) {
|
|
|
52
52
|
return 0;
|
|
53
53
|
}
|
|
54
54
|
|
|
55
|
+
// A registry answer whose cache write failed (a read-only home), by cache file.
|
|
56
|
+
// The write is best effort, so a good fetch must not depend on it.
|
|
57
|
+
const fetchedInProcess = new Map();
|
|
58
|
+
|
|
59
|
+
// The newer of the cache file and this process's own last fetch.
|
|
55
60
|
function readCache(cacheFile) {
|
|
61
|
+
let disk = null;
|
|
56
62
|
try {
|
|
57
|
-
|
|
63
|
+
disk = JSON.parse(readFileSync(cacheFile, "utf8"));
|
|
58
64
|
} catch {
|
|
59
|
-
|
|
65
|
+
/* missing or unreadable: the in-process answer, if any, stands */
|
|
60
66
|
}
|
|
67
|
+
const memory = fetchedInProcess.get(cacheFile) ?? null;
|
|
68
|
+
if (!disk) return memory;
|
|
69
|
+
return memory && !(memory.checkedAt <= disk.checkedAt) ? memory : disk;
|
|
61
70
|
}
|
|
62
71
|
|
|
63
72
|
function writeCache(cacheFile, cacheDir, latest) {
|
|
73
|
+
const entry = { latest, checkedAt: Date.now() };
|
|
64
74
|
try {
|
|
65
75
|
mkdirSync(cacheDir, { recursive: true });
|
|
66
|
-
writeFileSync(cacheFile, JSON.stringify(
|
|
76
|
+
writeFileSync(cacheFile, JSON.stringify(entry, null, "\t"));
|
|
77
|
+
fetchedInProcess.delete(cacheFile);
|
|
67
78
|
} catch {
|
|
68
|
-
|
|
79
|
+
// A read-only home is not a reason to fail a local dev server, nor to forget the answer.
|
|
80
|
+
fetchedInProcess.set(cacheFile, entry);
|
|
69
81
|
}
|
|
70
82
|
}
|
|
71
83
|
|
|
@@ -134,11 +146,47 @@ export function readUpdateStatus(currentVersion, stateDir) {
|
|
|
134
146
|
}
|
|
135
147
|
}
|
|
136
148
|
|
|
149
|
+
// `--no-update-check`, COZYCLAY_NO_UPDATE_CHECK and CI each turn the notice,
|
|
150
|
+
// the Studio chip and the one-click install off.
|
|
151
|
+
export function updateChecksOn(flag, env = process.env) {
|
|
152
|
+
return Boolean(flag && !env.CI && !env.COZYCLAY_NO_UPDATE_CHECK);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* The registry's latest x.y.z with this launch's status: the shape behind
|
|
157
|
+
* GET /__cozyclay/update and `cclay update --check`. The cache rules are
|
|
158
|
+
* checkForUpdate's; `fresh` asks the registry even inside the TTL, and a dead
|
|
159
|
+
* registry still falls back to the cached answer.
|
|
160
|
+
*/
|
|
161
|
+
export async function lookupLatest(currentVersion, stateDir, { fresh = false } = {}) {
|
|
162
|
+
const cacheFile = join(stateDir, "update-check.json");
|
|
163
|
+
if (fresh) {
|
|
164
|
+
const fetched = await fetchLatest();
|
|
165
|
+
if (fetched) writeCache(cacheFile, stateDir, fetched);
|
|
166
|
+
} else {
|
|
167
|
+
await checkForUpdate(currentVersion, stateDir);
|
|
168
|
+
}
|
|
169
|
+
const latest = readCache(cacheFile)?.latest;
|
|
170
|
+
const known = typeof latest === "string" && /^\d+\.\d+\.\d+/.test(latest);
|
|
171
|
+
return { latest: known ? latest : null, status: readUpdateStatus(currentVersion, stateDir) };
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* `cclay update --check`: print, install nothing, and exit 0 when this is the
|
|
176
|
+
* newest release, 10 when a newer one exists, 11 when the registry could not
|
|
177
|
+
* answer and nothing is cached. Scripts and agents branch on the code.
|
|
178
|
+
*/
|
|
179
|
+
export async function runUpdateCheck({ currentVersion, kind, stateDir }) {
|
|
180
|
+
const { latest, status } = await lookupLatest(currentVersion, stateDir, { fresh: true });
|
|
181
|
+
console.log(`current: ${currentVersion}\nlatest: ${latest ?? "unknown"}\nkind: ${kind}\nstatus: ${status}`);
|
|
182
|
+
return { latest: 0, outdated: 10 }[status] ?? 11;
|
|
183
|
+
}
|
|
184
|
+
|
|
137
185
|
// npx/bunx runs from a temp dir, so there is no local install to upgrade in
|
|
138
186
|
// place; `npm install -g` is the one command that is right either way.
|
|
139
187
|
export function runUpdate() {
|
|
140
188
|
const npm = process.platform === "win32" ? "npm.cmd" : "npm";
|
|
141
|
-
const child = spawn(npm, ["install", "-g", "cozyclay@latest"], { stdio: "inherit", shell: process.platform === "win32" });
|
|
189
|
+
const child = spawn(npm, ["install", "-g", "cozyclay@latest", "--engine-strict"], { stdio: "inherit", shell: process.platform === "win32" });
|
|
142
190
|
child.on("error", (err) => {
|
|
143
191
|
console.error(`cozyclay: could not run npm: ${err.message}`);
|
|
144
192
|
process.exit(1);
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The one-click global update behind POST /__cozyclay/update.
|
|
3
|
+
*
|
|
4
|
+
* Two questions live here: may this launcher update itself at all
|
|
5
|
+
* (canSelfUpdate), and what happened when it tried (an in-memory install
|
|
6
|
+
* state the Studio polls). Nothing a request carries ever reaches npm: the
|
|
7
|
+
* only argument that varies is a version that has passed an exact x.y.z test.
|
|
8
|
+
*/
|
|
9
|
+
import { execFile, spawn } from "node:child_process";
|
|
10
|
+
import { constants, accessSync, realpathSync } from "node:fs";
|
|
11
|
+
import { join } from "node:path";
|
|
12
|
+
|
|
13
|
+
const VERSION = /^\d+\.\d+\.\d+$/;
|
|
14
|
+
const ROOT_TIMEOUT_MS = 2000;
|
|
15
|
+
const INSTALL_TIMEOUT_MS = 5 * 60 * 1000;
|
|
16
|
+
const STDERR_CAP = 64 * 1024;
|
|
17
|
+
|
|
18
|
+
const ERROR_CODES = [
|
|
19
|
+
["node_version", ["EBADENGINE"]],
|
|
20
|
+
["permission", ["EACCES", "EPERM"]],
|
|
21
|
+
["network", ["ENOTFOUND", "ETIMEDOUT", "ECONNREFUSED", "ECONNRESET", "EAI_AGAIN"]],
|
|
22
|
+
];
|
|
23
|
+
|
|
24
|
+
// COZYCLAY_NPM_BIN lets a test put a fake npm in front of both calls; win32
|
|
25
|
+
// keeps npm.cmd the way runUpdate spawns it.
|
|
26
|
+
function npmBinary(platform = process.platform) {
|
|
27
|
+
return process.env.COZYCLAY_NPM_BIN || (platform === "win32" ? "npm.cmd" : "npm");
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function npmRootGlobal(npmBin, platform) {
|
|
31
|
+
return new Promise((resolve) => {
|
|
32
|
+
execFile(npmBin, ["root", "-g"], { timeout: ROOT_TIMEOUT_MS, shell: platform === "win32", encoding: "utf8" }, (error, stdout) => {
|
|
33
|
+
resolve(error ? null : String(stdout).trim() || null);
|
|
34
|
+
});
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const selfUpdateCache = new Map();
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* True only when all three hold: not Windows (U1 scope), this package's real
|
|
42
|
+
* path is `<npm root -g>/cozyclay`, and that folder and its parent are
|
|
43
|
+
* writable. Any doubt answers false, which falls back to a copied command.
|
|
44
|
+
* Computed once per (pkgRoot, platform, npm) and kept in memory.
|
|
45
|
+
*/
|
|
46
|
+
export function canSelfUpdate({ pkgRoot, platform = process.platform, npmBin = npmBinary(platform) }) {
|
|
47
|
+
const key = JSON.stringify([pkgRoot, platform, npmBin]);
|
|
48
|
+
if (!selfUpdateCache.has(key)) selfUpdateCache.set(key, computeSelfUpdate(pkgRoot, platform, npmBin));
|
|
49
|
+
return selfUpdateCache.get(key);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
async function computeSelfUpdate(pkgRoot, platform, npmBin) {
|
|
53
|
+
if (platform === "win32") return false;
|
|
54
|
+
const root = await npmRootGlobal(npmBin, platform);
|
|
55
|
+
if (!root) return false;
|
|
56
|
+
try {
|
|
57
|
+
const installed = realpathSync(join(root, "cozyclay"));
|
|
58
|
+
if (realpathSync(pkgRoot) !== installed) return false;
|
|
59
|
+
// npm swaps the folder, so the parent has to take writes as well.
|
|
60
|
+
accessSync(installed, constants.W_OK);
|
|
61
|
+
accessSync(realpathSync(root), constants.W_OK);
|
|
62
|
+
return true;
|
|
63
|
+
} catch {
|
|
64
|
+
return false;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function classOf(code) {
|
|
69
|
+
return ERROR_CODES.find(([, codes]) => codes.includes(code))?.[0] ?? null;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* permission | network | node_version | npm_missing | other, from npm's
|
|
74
|
+
* stderr and the spawn error (if any). The `npm error code X` line wins;
|
|
75
|
+
* otherwise the first known code anywhere in the text decides.
|
|
76
|
+
*/
|
|
77
|
+
export function classifyNpmError(stderr = "", spawnError = null) {
|
|
78
|
+
if (spawnError?.code === "ENOENT") return "npm_missing";
|
|
79
|
+
const text = String(stderr ?? "");
|
|
80
|
+
const lineCode = /npm (?:error|ERR!) code (\S+)/.exec(text)?.[1];
|
|
81
|
+
if (lineCode && classOf(lineCode)) return classOf(lineCode);
|
|
82
|
+
if (/Not compatible with your version of node/i.test(text)) return "node_version";
|
|
83
|
+
for (const token of text.match(/\bE[A-Z_]{2,}\b/g) ?? []) {
|
|
84
|
+
if (classOf(token)) return classOf(token);
|
|
85
|
+
}
|
|
86
|
+
return classOf(spawnError?.code) ?? "other";
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* The launcher's install state: { phase, errorClass, startedAt, durationMs }.
|
|
91
|
+
* startInstall is the lock: while a run is updating or has finished and waits
|
|
92
|
+
* for a restart, it starts nothing and returns false.
|
|
93
|
+
*/
|
|
94
|
+
export function createInstaller({ timeoutMs = INSTALL_TIMEOUT_MS } = {}) {
|
|
95
|
+
const state = { phase: "idle", errorClass: null, startedAt: null, durationMs: null };
|
|
96
|
+
let timer = null;
|
|
97
|
+
function finish(phase, errorClass) {
|
|
98
|
+
if (state.phase !== "updating") return;
|
|
99
|
+
clearTimeout(timer);
|
|
100
|
+
Object.assign(state, { phase, errorClass, durationMs: Date.now() - state.startedAt });
|
|
101
|
+
}
|
|
102
|
+
function startInstall(version) {
|
|
103
|
+
if (!VERSION.test(String(version))) throw new TypeError("cozyclay: refusing to install a version that is not x.y.z");
|
|
104
|
+
if (state.phase === "updating" || state.phase === "restart_needed") return false;
|
|
105
|
+
Object.assign(state, { phase: "updating", errorClass: null, startedAt: Date.now(), durationMs: null });
|
|
106
|
+
const platform = process.platform;
|
|
107
|
+
let child;
|
|
108
|
+
try {
|
|
109
|
+
child = spawn(npmBinary(platform), ["install", "-g", `cozyclay@${version}`, "--engine-strict"], {
|
|
110
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
111
|
+
shell: platform === "win32",
|
|
112
|
+
});
|
|
113
|
+
} catch (error) {
|
|
114
|
+
finish("failed", classifyNpmError("", error));
|
|
115
|
+
return true;
|
|
116
|
+
}
|
|
117
|
+
let stderr = "";
|
|
118
|
+
child.stdout.resume();
|
|
119
|
+
child.stderr.setEncoding("utf8");
|
|
120
|
+
child.stderr.on("data", (chunk) => {
|
|
121
|
+
if (stderr.length < STDERR_CAP) stderr += chunk.slice(0, STDERR_CAP - stderr.length);
|
|
122
|
+
});
|
|
123
|
+
timer = setTimeout(() => {
|
|
124
|
+
child.kill("SIGTERM");
|
|
125
|
+
finish("failed", "other");
|
|
126
|
+
}, timeoutMs);
|
|
127
|
+
timer.unref?.();
|
|
128
|
+
child.on("error", (error) => finish("failed", classifyNpmError(stderr, error)));
|
|
129
|
+
child.on("close", (code, signal) => {
|
|
130
|
+
if (code === 0 && !signal) finish("restart_needed", null);
|
|
131
|
+
else finish("failed", classifyNpmError(stderr));
|
|
132
|
+
});
|
|
133
|
+
return true;
|
|
134
|
+
}
|
|
135
|
+
return { startInstall, getState: () => ({ ...state }) };
|
|
136
|
+
}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GET and POST /__cozyclay/update: what the Studio's update chip reads, and
|
|
3
|
+
* the one click that installs. Every input is injected so the route runs
|
|
4
|
+
* under a test with a mock registry and a fake npm.
|
|
5
|
+
*
|
|
6
|
+
* POST reads no body. The version it installs is the registry's latest after
|
|
7
|
+
* an exact x.y.z test, never anything the request says.
|
|
8
|
+
*/
|
|
9
|
+
const VERSION = /^\d+\.\d+\.\d+$/;
|
|
10
|
+
const NOTES_URL = "https://github.com/NomaDamas/CozyClay/releases/tag/v";
|
|
11
|
+
|
|
12
|
+
function sendJson(res, status, body, headers = {}) {
|
|
13
|
+
res.writeHead(status, { "content-type": "application/json; charset=utf-8", "cache-control": "no-store", ...headers });
|
|
14
|
+
res.end(JSON.stringify(body));
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* @param {object} options
|
|
19
|
+
* @param {string} options.currentVersion the running package version
|
|
20
|
+
* @param {() => Promise<{latest: string|null, status: string}>} options.checkLatest
|
|
21
|
+
* @param {"global"|"npx"|"clone"} options.kind
|
|
22
|
+
* @param {boolean} options.enabled false when update checks are off
|
|
23
|
+
* @param {number} options.port the launcher's loopback port
|
|
24
|
+
* @param {() => Promise<boolean>} options.canSelfUpdate
|
|
25
|
+
* @param {{startInstall(version: string): boolean, getState(): object}} options.installer
|
|
26
|
+
*/
|
|
27
|
+
export function createUpdateRoutes({ currentVersion, checkLatest, kind, enabled, port, canSelfUpdate, installer }) {
|
|
28
|
+
const command = kind === "npx" ? "npx cozyclay" : "npm install -g cozyclay@latest";
|
|
29
|
+
const hosts = new Set([`127.0.0.1:${port}`, `localhost:${port}`]);
|
|
30
|
+
// Spelled in two parts so the G014 loopback invariant does not read it as a client URL.
|
|
31
|
+
const origins = new Set([`http://127.0.0.1:${port}`, `http://${"local" + "host"}:${port}`]);
|
|
32
|
+
|
|
33
|
+
async function info() {
|
|
34
|
+
const base = { enabled, current: currentVersion, latest: null, status: "unknown", kind, canSelfUpdate: false, phase: "idle", errorClass: null, durationMs: null, notesUrl: null, command };
|
|
35
|
+
if (!enabled) return base;
|
|
36
|
+
const [{ latest, status }, selfUpdate] = await Promise.all([
|
|
37
|
+
checkLatest().catch(() => ({ latest: null, status: "unknown" })),
|
|
38
|
+
kind === "global" ? canSelfUpdate().catch(() => false) : false,
|
|
39
|
+
]);
|
|
40
|
+
const { phase, errorClass, durationMs } = installer.getState();
|
|
41
|
+
const finished = phase === "restart_needed" || phase === "failed";
|
|
42
|
+
return {
|
|
43
|
+
...base,
|
|
44
|
+
latest: latest ?? null,
|
|
45
|
+
status: latest ? status : "unknown",
|
|
46
|
+
canSelfUpdate: selfUpdate === true,
|
|
47
|
+
phase,
|
|
48
|
+
errorClass: phase === "failed" ? errorClass : null,
|
|
49
|
+
durationMs: finished ? durationMs : null,
|
|
50
|
+
notesUrl: latest ? `${NOTES_URL}${latest}` : null,
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
async function handlePost(req, res) {
|
|
55
|
+
if (req.headers.origin !== `http://127.0.0.1:${port}`) return sendJson(res, 403, { error: "forbidden origin" });
|
|
56
|
+
if (!enabled) return sendJson(res, 403, { error: "update checks are disabled" });
|
|
57
|
+
const { phase } = installer.getState();
|
|
58
|
+
if (phase === "updating" || phase === "restart_needed") return sendJson(res, 202, await info());
|
|
59
|
+
const current = await info();
|
|
60
|
+
if (!(kind === "global" && current.canSelfUpdate && current.status === "outdated" && VERSION.test(current.latest ?? ""))) {
|
|
61
|
+
return sendJson(res, 409, { error: "self-update unavailable" });
|
|
62
|
+
}
|
|
63
|
+
installer.startInstall(current.latest);
|
|
64
|
+
return sendJson(res, 202, await info());
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
return async function handleUpdateRoute(req, res) {
|
|
68
|
+
try {
|
|
69
|
+
if (req.method === "GET") {
|
|
70
|
+
const origin = req.headers.origin;
|
|
71
|
+
if (!(origins.has(origin) || (origin === undefined && hosts.has(req.headers.host)))) return sendJson(res, 403, { error: "forbidden origin" });
|
|
72
|
+
return sendJson(res, 200, await info());
|
|
73
|
+
}
|
|
74
|
+
if (req.method === "POST") return await handlePost(req, res);
|
|
75
|
+
return sendJson(res, 405, { error: "method not allowed" }, { allow: "GET, POST" });
|
|
76
|
+
} catch {
|
|
77
|
+
if (!res.headersSent) sendJson(res, 500, { error: "update unavailable" });
|
|
78
|
+
}
|
|
79
|
+
};
|
|
80
|
+
}
|
|
@@ -168,7 +168,7 @@
|
|
|
168
168
|
<div class="wrap">
|
|
169
169
|
<a class="brand" href="/">CozyClay</a>
|
|
170
170
|
<nav>
|
|
171
|
-
<a href="
|
|
171
|
+
<a href="/app/">Try it</a>
|
|
172
172
|
<a href="/greybox-to-video/">Greybox to video</a>
|
|
173
173
|
<a href="/previs-software/">Previs software</a>
|
|
174
174
|
<a href="/seedance-camera-control/">Seedance</a>
|
package/dist/app/index.html
CHANGED
|
@@ -34,15 +34,14 @@
|
|
|
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-
|
|
37
|
+
<script type="module" crossorigin src="/assets/app-CTKcyPM-.js"></script>
|
|
38
38
|
<link rel="modulepreload" crossorigin href="/assets/rolldown-runtime-hePW80VL.js">
|
|
39
|
-
<link rel="modulepreload" crossorigin href="/assets/
|
|
40
|
-
<link rel="modulepreload" crossorigin href="/assets/task-board-DFfNa0xg.js">
|
|
39
|
+
<link rel="modulepreload" crossorigin href="/assets/task-board-CkLZGPkv.js">
|
|
41
40
|
<link rel="modulepreload" crossorigin href="/assets/fflate.module-KbGj5-H6.js">
|
|
42
|
-
<link rel="modulepreload" crossorigin href="/assets/scene-asset-sync-
|
|
43
|
-
<link rel="modulepreload" crossorigin href="/assets/motion3-compile-
|
|
41
|
+
<link rel="modulepreload" crossorigin href="/assets/scene-asset-sync-t45pWCyq.js">
|
|
42
|
+
<link rel="modulepreload" crossorigin href="/assets/motion3-compile-Du7UXrWn.js">
|
|
44
43
|
<link rel="stylesheet" crossorigin href="/assets/scene-asset-sync-BEjjhNEo.css">
|
|
45
|
-
<link rel="stylesheet" crossorigin href="/assets/app-
|
|
44
|
+
<link rel="stylesheet" crossorigin href="/assets/app-CEP7jzXB.css">
|
|
46
45
|
</head>
|
|
47
46
|
<body>
|
|
48
47
|
<div id="root">
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{r as e}from"./rolldown-runtime-hePW80VL.js";import{
|
|
1
|
+
import{r as e}from"./rolldown-runtime-hePW80VL.js";import{Fa as t,Ji as n,c as r,d as i,f as a,l as o,m as s,o as c,v as l}from"./task-board-CkLZGPkv.js";import{o as u,r as d,t as f}from"./motion3-compile-Du7UXrWn.js";var p=e(t(),1),m=2e3,h=(e,t)=>new Promise(n=>{let r=setTimeout(n,e);t.addEventListener(`abort`,()=>{clearTimeout(r),n()},{once:!0})});function g(e,t){(0,p.useEffect)(()=>{let r=new Map;for(let[n,i]of Object.entries(e))for(let e of i)r.set(e,t[n]??n);let i=()=>{for(let e of document.querySelectorAll(`[data-node-id]`)){let t=r.get(e.getAttribute(`data-node-id`));t?(e.setAttribute(`data-task-scope`,t),e.setAttribute(`title`,n(`In task: ${t}`,`작업 중: ${t}`,`任务中:${t}`))):e.hasAttribute(`data-task-scope`)&&(e.removeAttribute(`data-task-scope`),e.removeAttribute(`title`))}};i();let a=new MutationObserver(i);return a.observe(document.body,{childList:!0,subtree:!0}),()=>{a.disconnect(),r.clear(),i()}},[e,t])}function _({sessionId:e,pulse:t,labelOf:n,onMainNote:_}){let v=(0,p.useMemo)(()=>c(),[]),y=u(),[b,x]=(0,p.useReducer)((e,t)=>t.seed?s(e,t.seed):t.reset?r():i(e,t.event),void 0,r),[S,C]=(0,p.useState)({}),[w,T]=(0,p.useState)(f),E=(0,p.useRef)(0),D=(0,p.useRef)(_);(0,p.useEffect)(()=>{E.current=b.after,D.current=_},[b.after,_]),(0,p.useEffect)(()=>{x({reset:!0}),E.current=0},[e]),(0,p.useEffect)(()=>d(T),[]),(0,p.useEffect)(()=>y.subscribe(e=>{typeof e?.type!=`string`||!e.type.startsWith(`task.`)||(C(t=>a(t,e)),(e.type===`task.userEdit`||e.type===`task.undone`)&&x({event:e}))}),[y]),(0,p.useEffect)(()=>{if(!e)return;let t=new AbortController;return(async()=>{for(;!t.signal.aborted;){try{let{tasks:n}=await v.board(e);x({seed:n}),await v.events(e,E.current,e=>{if(e.type===`main.turn`&&e.turnId){let n=``;v.frames(e.turnId,e=>{e.type===`text.delta`&&(n+=e.text??``),e.type===`tool.start`&&(n=``)},t.signal).then(()=>D.current?.(n),()=>{})}x({event:e})},t.signal)}catch{}await h(m,t.signal)}})(),()=>t.abort()},[v,e,t]);let O=b.order.filter(e=>b.tasks[e]?.status===`running`&&b.tasks[e]?.turnId),k=O.map(e=>`${e}:${b.tasks[e].turnId}`).join();return(0,p.useEffect)(()=>{let e=O.map(e=>{let t=new AbortController,{turnId:r}=b.tasks[e];return v.frames(r,t=>{let r=o(t,n);r&&x({event:{type:`task.progress`,taskId:e,status:`running`,progress:r}})},t.signal).catch(()=>{}),t});return()=>e.forEach(e=>e.abort())},[v,k,n]),g(S,(0,p.useMemo)(()=>Object.fromEntries(b.order.map(e=>[e,b.tasks[e].title])),[b])),(0,p.useEffect)(()=>{l.set({client:v,labelOf:n,undo:async e=>{let t=await y.run(`task.undo`,{taskId:e});if(t?.ok===!1)throw Error(t.message??t.code);x({event:{type:`task.undone`,taskId:e}})}})},[y,v,n]),(0,p.useEffect)(()=>{l.set({board:b,activity:w})},[b,w]),(0,p.useEffect)(()=>()=>l.set({board:r(),open:null}),[e]),null}export{_ as default};
|