cozyclay 1.0.0 → 1.1.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/bin/cozyclay.mjs +85 -5
- package/package.json +1 -1
package/bin/cozyclay.mjs
CHANGED
|
@@ -15,9 +15,11 @@
|
|
|
15
15
|
* before it can serve a prebuilt app is a launcher that will break.
|
|
16
16
|
*/
|
|
17
17
|
import { spawn } from "node:child_process";
|
|
18
|
-
import { createReadStream, existsSync, statSync } from "node:fs";
|
|
18
|
+
import { createReadStream, existsSync, mkdirSync, readFileSync, statSync, writeFileSync } from "node:fs";
|
|
19
19
|
import { createServer, request as httpRequest } from "node:http";
|
|
20
|
+
import { homedir } from "node:os";
|
|
20
21
|
import { extname, join, normalize, resolve } from "node:path";
|
|
22
|
+
import { createInterface } from "node:readline";
|
|
21
23
|
import { fileURLToPath } from "node:url";
|
|
22
24
|
|
|
23
25
|
const PKG_ROOT = resolve(fileURLToPath(new URL("..", import.meta.url)));
|
|
@@ -43,7 +45,7 @@ const TYPES = {
|
|
|
43
45
|
};
|
|
44
46
|
|
|
45
47
|
function parseArgs(argv) {
|
|
46
|
-
const opts = { port: 5180, host: "127.0.0.1", ardy: true, open: true };
|
|
48
|
+
const opts = { port: 5180, host: "127.0.0.1", ardy: true, open: true, star: true };
|
|
47
49
|
for (let i = 0; i < argv.length; i += 1) {
|
|
48
50
|
const arg = argv[i];
|
|
49
51
|
if (arg === "--port" || arg === "-p") opts.port = Number(argv[++i]);
|
|
@@ -52,6 +54,7 @@ function parseArgs(argv) {
|
|
|
52
54
|
else if (arg.startsWith("--host=")) opts.host = arg.slice(7);
|
|
53
55
|
else if (arg === "--no-ardy") opts.ardy = false;
|
|
54
56
|
else if (arg === "--no-open") opts.open = false;
|
|
57
|
+
else if (arg === "--no-star") opts.star = false;
|
|
55
58
|
else if (arg === "--help" || arg === "-h") opts.help = true;
|
|
56
59
|
else if (arg === "--version" || arg === "-v") opts.version = true;
|
|
57
60
|
else {
|
|
@@ -66,12 +69,20 @@ function parseArgs(argv) {
|
|
|
66
69
|
return opts;
|
|
67
70
|
}
|
|
68
71
|
|
|
72
|
+
const REPO_URL = "https://github.com/HaD0Yun/CozyClay";
|
|
73
|
+
const STATE_DIR = join(process.env.XDG_CONFIG_HOME || join(homedir(), ".config"), "cozyclay");
|
|
74
|
+
const STATE_FILE = join(STATE_DIR, "state.json");
|
|
75
|
+
// Only worth asking someone who actually used the thing. A prompt three
|
|
76
|
+
// seconds in is a popup; a prompt after a real session is a question.
|
|
77
|
+
const STAR_AFTER_MS = Number(process.env.COZYCLAY_STAR_AFTER_MS ?? 60_000);
|
|
78
|
+
|
|
69
79
|
const HELP = `cozyclay - browser-based 3D staging studio
|
|
70
80
|
|
|
71
81
|
npx cozyclay start the studio and open it
|
|
72
82
|
npx cozyclay --port 5200 serve on another port
|
|
73
83
|
npx cozyclay --no-ardy skip the optional motion-generation sidecar
|
|
74
84
|
npx cozyclay --no-open do not open a browser
|
|
85
|
+
npx cozyclay --no-star never ask about starring the repo
|
|
75
86
|
|
|
76
87
|
Motion generation needs an SSH-reachable NVIDIA machine running ARDY; point
|
|
77
88
|
the sidecar at it with CCLAY_ARDY_HOST. Everything else - staging, posing,
|
|
@@ -108,6 +119,66 @@ function proxyToBridge(req, res) {
|
|
|
108
119
|
req.pipe(upstream);
|
|
109
120
|
}
|
|
110
121
|
|
|
122
|
+
function readState() {
|
|
123
|
+
try {
|
|
124
|
+
return JSON.parse(readFileSync(STATE_FILE, "utf8"));
|
|
125
|
+
} catch {
|
|
126
|
+
return {};
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
function writeState(patch) {
|
|
131
|
+
try {
|
|
132
|
+
mkdirSync(STATE_DIR, { recursive: true });
|
|
133
|
+
writeFileSync(STATE_FILE, JSON.stringify({ ...readState(), ...patch }, null, "\t"));
|
|
134
|
+
} catch {
|
|
135
|
+
/* a read-only home is not a reason to fail a local dev server */
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
async function starCount() {
|
|
140
|
+
try {
|
|
141
|
+
const res = await fetch("https://api.github.com/repos/HaD0Yun/CozyClay", {
|
|
142
|
+
headers: { accept: "application/vnd.github+json" },
|
|
143
|
+
signal: AbortSignal.timeout(2500),
|
|
144
|
+
});
|
|
145
|
+
if (!res.ok) return null;
|
|
146
|
+
const body = await res.json();
|
|
147
|
+
return Number.isInteger(body?.stargazers_count) ? body.stargazers_count : null;
|
|
148
|
+
} catch {
|
|
149
|
+
return null;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// Asked once, on the way out, and never again on this machine. A CLI that
|
|
154
|
+
// nags on every run is worse than one that never asks.
|
|
155
|
+
async function maybeAskForStar(startedAt, opts) {
|
|
156
|
+
if (!opts.star) return;
|
|
157
|
+
if (process.env.CI || process.env.COZYCLAY_NO_STAR) return;
|
|
158
|
+
if (!process.stdin.isTTY || !process.stdout.isTTY) return;
|
|
159
|
+
if (Date.now() - startedAt < STAR_AFTER_MS) return;
|
|
160
|
+
if (readState().starPromptedAt) return;
|
|
161
|
+
|
|
162
|
+
writeState({ starPromptedAt: new Date().toISOString() });
|
|
163
|
+
const stars = await starCount();
|
|
164
|
+
const tally = stars === null ? "" : ` (${stars} so far)`;
|
|
165
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
166
|
+
const answer = await new Promise((done) => {
|
|
167
|
+
const timer = setTimeout(() => done(""), 12_000);
|
|
168
|
+
rl.question(`\nWas CozyClay any good? A star helps people find it${tally}. Open GitHub? [Y/n] `, (a) => {
|
|
169
|
+
clearTimeout(timer);
|
|
170
|
+
done(a);
|
|
171
|
+
});
|
|
172
|
+
});
|
|
173
|
+
rl.close();
|
|
174
|
+
if (/^(y|yes|)$/i.test(answer.trim())) {
|
|
175
|
+
openBrowser(REPO_URL);
|
|
176
|
+
console.log("Thanks. Opening the repo.");
|
|
177
|
+
} else {
|
|
178
|
+
console.log(`Fair enough. ${REPO_URL} if you change your mind.`);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
111
182
|
function openBrowser(url) {
|
|
112
183
|
const cmd =
|
|
113
184
|
process.platform === "darwin" ? "open" : process.platform === "win32" ? "start" : "xdg-open";
|
|
@@ -163,10 +234,19 @@ const server = createServer((req, res) => {
|
|
|
163
234
|
serveFile(res, target);
|
|
164
235
|
});
|
|
165
236
|
|
|
166
|
-
|
|
237
|
+
const startedAt = Date.now();
|
|
238
|
+
let shuttingDown = false;
|
|
239
|
+
async function shutdown() {
|
|
240
|
+
if (shuttingDown) process.exit(0);
|
|
241
|
+
shuttingDown = true;
|
|
167
242
|
if (bridge) bridge.kill("SIGTERM");
|
|
168
|
-
server.close(
|
|
169
|
-
|
|
243
|
+
server.close();
|
|
244
|
+
try {
|
|
245
|
+
await maybeAskForStar(startedAt, opts);
|
|
246
|
+
} catch {
|
|
247
|
+
/* never let the goodbye prompt hold the process hostage */
|
|
248
|
+
}
|
|
249
|
+
process.exit(0);
|
|
170
250
|
}
|
|
171
251
|
process.on("SIGINT", shutdown);
|
|
172
252
|
process.on("SIGTERM", shutdown);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cozyclay",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"description": "Browser-based 3D staging studio: block a scene, pose characters, author camera moves and cuts, and preview generated motion.",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"dev": "node tools/dev-full.mjs --host 127.0.0.1 --port 5180",
|