@genex-ai/cli-demo 0.48.0-dev.91 → 0.49.0-dev.92
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/README.md +3 -0
- package/dist/index.js +120 -0
- package/package.json +1 -1
- package/templates/skills/genex-getting-started/SKILL.md +4 -0
package/README.md
CHANGED
|
@@ -6,6 +6,7 @@ This is the `cli` app of the [genex monorepo](../../README.md).
|
|
|
6
6
|
```bash
|
|
7
7
|
genex init <name> # authorize + create the draft project
|
|
8
8
|
genex link <slug> # re-link this folder to an existing game of yours (recovery)
|
|
9
|
+
genex list # list your games — slug, status, page link (find the slug to link)
|
|
9
10
|
genex preview # build + push to the hosted draft URL (unlisted)
|
|
10
11
|
genex publish # build + push, then list the game in the gallery
|
|
11
12
|
genex make-remixable # make this game remixable — migrate a private source to a public repo
|
|
@@ -15,9 +16,11 @@ genex sfx "<prompt>" # generate a sound fx → prints an asset URL
|
|
|
15
16
|
genex texture "<prompt>" # generate a texture → prints an asset URL
|
|
16
17
|
genex image "<prompt>" # generate an image → prints an asset URL
|
|
17
18
|
genex video "<prompt>" # generate a video → prints an asset URL
|
|
19
|
+
genex wait <id> # attach to a --no-wait generation and print its URL(s) when done
|
|
18
20
|
genex controller <type> # character|car|drone|networked-physics → src/controllers/
|
|
19
21
|
genex controller anims <sel…> # download extra character animation clips (by tag or name) → public/assets/anims/
|
|
20
22
|
genex ui <tool> # local pixel toolbox for generated UI art: extract | masks | text-color | trim
|
|
23
|
+
genex explore ["<query>"] # search the curated community gallery (no query lists the whole catalog)
|
|
21
24
|
```
|
|
22
25
|
|
|
23
26
|
> **Invoking it.** First-time setup runs via `npx @genex-ai/cli-demo@latest init`.
|
package/dist/index.js
CHANGED
|
@@ -1328,6 +1328,113 @@ async function listOwnSlugs(apiUrl, token, log) {
|
|
|
1328
1328
|
}
|
|
1329
1329
|
}
|
|
1330
1330
|
|
|
1331
|
+
// src/commands/list.ts
|
|
1332
|
+
async function runList(opts) {
|
|
1333
|
+
const log = createLogger({ quiet: opts.quiet });
|
|
1334
|
+
const extra = opts.name?.trim();
|
|
1335
|
+
if (extra) {
|
|
1336
|
+
log.error(
|
|
1337
|
+
`\`genex list\` lists your games \u2014 \`genex list ${extra}\` isn't available yet.`
|
|
1338
|
+
);
|
|
1339
|
+
process.exitCode = 1;
|
|
1340
|
+
return;
|
|
1341
|
+
}
|
|
1342
|
+
let token = opts.token ?? await readUserToken(opts.envPath);
|
|
1343
|
+
if (!token) {
|
|
1344
|
+
if (opts.noAuth) {
|
|
1345
|
+
log.error("Not signed in. Re-run without --no-auth to connect in the browser.");
|
|
1346
|
+
process.exitCode = 1;
|
|
1347
|
+
return;
|
|
1348
|
+
}
|
|
1349
|
+
log.plain("Not signed in \u2014 opening the browser to connect\u2026");
|
|
1350
|
+
try {
|
|
1351
|
+
token = await authorize(getAuthUrl(opts.authUrl), {
|
|
1352
|
+
log,
|
|
1353
|
+
timeoutMs: opts.timeoutSec ? opts.timeoutSec * 1e3 : void 0
|
|
1354
|
+
});
|
|
1355
|
+
} catch (err) {
|
|
1356
|
+
log.error(`Sign-in didn't complete: ${String(err)}`);
|
|
1357
|
+
process.exitCode = 1;
|
|
1358
|
+
return;
|
|
1359
|
+
}
|
|
1360
|
+
const { path: tokenPath } = await writeUserToken(token, opts.envPath);
|
|
1361
|
+
log.success(`Authorized. Saved your token to ${c.cyan(tokenPath)}.`);
|
|
1362
|
+
log.plain("");
|
|
1363
|
+
}
|
|
1364
|
+
const meta = await readProject();
|
|
1365
|
+
const apiUrl = getApiUrl(opts.apiUrl ?? meta?.apiUrl);
|
|
1366
|
+
const dashOrigin = (meta?.dashboardOrigins?.[0] ?? getAuthUrl(opts.authUrl)).replace(/\/+$/, "");
|
|
1367
|
+
const projectsUrl = `${apiUrl}/api/projects`;
|
|
1368
|
+
const fetchProjects = (t) => apiFetch(projectsUrl, { headers: { Authorization: `Bearer ${t}` } });
|
|
1369
|
+
let res;
|
|
1370
|
+
try {
|
|
1371
|
+
res = await fetchProjects(token);
|
|
1372
|
+
if (res.status === 401 && !opts.token && !opts.noAuth) {
|
|
1373
|
+
log.plain("Your saved sign-in was rejected \u2014 reconnecting in the browser\u2026");
|
|
1374
|
+
token = await authorize(getAuthUrl(opts.authUrl), {
|
|
1375
|
+
log,
|
|
1376
|
+
timeoutMs: opts.timeoutSec ? opts.timeoutSec * 1e3 : void 0
|
|
1377
|
+
});
|
|
1378
|
+
await writeUserToken(token, opts.envPath);
|
|
1379
|
+
res = await fetchProjects(token);
|
|
1380
|
+
}
|
|
1381
|
+
} catch (err) {
|
|
1382
|
+
log.error(`Couldn't reach the API at ${apiUrl}.`);
|
|
1383
|
+
log.dim(` ${String(err)}`);
|
|
1384
|
+
process.exitCode = 1;
|
|
1385
|
+
return;
|
|
1386
|
+
}
|
|
1387
|
+
if (!res.ok) {
|
|
1388
|
+
if (!printedStructuredError(res)) {
|
|
1389
|
+
if (res.status === 401) {
|
|
1390
|
+
log.error("Not authorized \u2014 your sign-in was rejected. Re-run `genex list` to reconnect.");
|
|
1391
|
+
} else {
|
|
1392
|
+
log.error(`Couldn't list your games (HTTP ${res.status}).`);
|
|
1393
|
+
}
|
|
1394
|
+
}
|
|
1395
|
+
process.exitCode = 1;
|
|
1396
|
+
return;
|
|
1397
|
+
}
|
|
1398
|
+
const body = await res.json().catch(() => null);
|
|
1399
|
+
const projects = body?.projects ?? [];
|
|
1400
|
+
if (opts.json) {
|
|
1401
|
+
console.log(JSON.stringify(projects, null, 2));
|
|
1402
|
+
return;
|
|
1403
|
+
}
|
|
1404
|
+
const email = await fetchSignedInEmail(apiUrl, token);
|
|
1405
|
+
if (email) log.plain(` signed in as ${c.cyan(email)}`);
|
|
1406
|
+
if (projects.length === 0) {
|
|
1407
|
+
log.plain("You have no games yet \u2014 `npx genex init <name>` in a new folder creates one.");
|
|
1408
|
+
return;
|
|
1409
|
+
}
|
|
1410
|
+
log.plain("");
|
|
1411
|
+
for (const p of projects) {
|
|
1412
|
+
const published = p.status === "published";
|
|
1413
|
+
const pageUrl = `${dashOrigin}/${published ? "world" : "draft"}/${p.slug}`;
|
|
1414
|
+
const isHere = Boolean(meta && (meta.id === p.id || meta.slug === p.slug));
|
|
1415
|
+
const bits = [
|
|
1416
|
+
published ? c.green("published") : c.dim("draft"),
|
|
1417
|
+
published && p.playsCount ? `${p.playsCount} play${p.playsCount === 1 ? "" : "s"}` : "",
|
|
1418
|
+
p.private ? "remixing off" : "",
|
|
1419
|
+
p.purchaseUrl ? "paid" : "",
|
|
1420
|
+
p.updatedAt ? `updated ${relTime(p.updatedAt)}` : ""
|
|
1421
|
+
].filter(Boolean).join(" \xB7 ");
|
|
1422
|
+
log.plain(` ${c.bold(p.slug)} ${bits}${isHere ? c.green(" \u2190 this folder") : ""}`);
|
|
1423
|
+
log.dim(` ${pageUrl}`);
|
|
1424
|
+
}
|
|
1425
|
+
if (projects.length === 100) {
|
|
1426
|
+
log.dim(" (showing your 100 most recent games)");
|
|
1427
|
+
}
|
|
1428
|
+
}
|
|
1429
|
+
function relTime(iso) {
|
|
1430
|
+
const ms = Date.parse(iso);
|
|
1431
|
+
if (Number.isNaN(ms)) return "recently";
|
|
1432
|
+
const s = Math.max(0, (Date.now() - ms) / 1e3);
|
|
1433
|
+
if (s < 3600) return `${Math.max(1, Math.floor(s / 60))}m ago`;
|
|
1434
|
+
if (s < 86400) return `${Math.floor(s / 3600)}h ago`;
|
|
1435
|
+
return `${Math.floor(s / 86400)}d ago`;
|
|
1436
|
+
}
|
|
1437
|
+
|
|
1331
1438
|
// src/lib/deploy.ts
|
|
1332
1439
|
import { spawn as spawn3 } from "child_process";
|
|
1333
1440
|
import crypto3 from "crypto";
|
|
@@ -3810,6 +3917,9 @@ ${c.bold("Usage")}
|
|
|
3810
3917
|
genex link <slug> [options] Re-link THIS folder to an existing game of yours
|
|
3811
3918
|
(lost folder / new machine); preview/publish then
|
|
3812
3919
|
update the same live game. Never creates a project.
|
|
3920
|
+
genex list [options] List your games \u2014 slug, status, and page link for
|
|
3921
|
+
each. Signs you in if needed; --json for raw data.
|
|
3922
|
+
Find the slug to reconnect with 'genex link' here.
|
|
3813
3923
|
genex preview [options] Build + push to the hosted draft URL (unlisted).
|
|
3814
3924
|
genex publish [options] Build + push, then list the game in the gallery.
|
|
3815
3925
|
genex make-remixable [options] Make THIS game remixable by everyone \u2014 migrates a
|
|
@@ -3908,6 +4018,12 @@ ${c.bold("Options for `explore`")}
|
|
|
3908
4018
|
--api-url <url> Override the API base URL.
|
|
3909
4019
|
(No query at all lists the entire curated catalog.)
|
|
3910
4020
|
|
|
4021
|
+
${c.bold("Options for `list`")}
|
|
4022
|
+
--json Print your games as JSON (all fields, incl. play/clone URLs).
|
|
4023
|
+
--api-url <url> Override the API base URL.
|
|
4024
|
+
--env <path> Token env file (default: ~/.genex/env).
|
|
4025
|
+
--auth-url <url> Override the auth site (used only if sign-in is needed).
|
|
4026
|
+
|
|
3911
4027
|
${UI_USAGE}
|
|
3912
4028
|
|
|
3913
4029
|
${c.bold("Global")}
|
|
@@ -3925,6 +4041,7 @@ ${c.bold("Environment")}
|
|
|
3925
4041
|
${c.bold("Examples")}
|
|
3926
4042
|
genex init my-game
|
|
3927
4043
|
genex init my-game --api-url http://localhost:3000 --auth-url http://localhost:5173
|
|
4044
|
+
genex list
|
|
3928
4045
|
genex link my-game
|
|
3929
4046
|
genex preview
|
|
3930
4047
|
genex publish
|
|
@@ -4280,6 +4397,9 @@ async function main() {
|
|
|
4280
4397
|
case "link":
|
|
4281
4398
|
await runLink(parsed.options);
|
|
4282
4399
|
break;
|
|
4400
|
+
case "list":
|
|
4401
|
+
await runList(parsed.options);
|
|
4402
|
+
break;
|
|
4283
4403
|
case "make-remixable":
|
|
4284
4404
|
await runMakeRemixable(parsed.options);
|
|
4285
4405
|
break;
|
package/package.json
CHANGED
|
@@ -128,6 +128,10 @@ npm install
|
|
|
128
128
|
npx @genex-ai/cli-demo@dev link <slug> # slug = the name in the play URL
|
|
129
129
|
```
|
|
130
130
|
|
|
131
|
+
Don't know the slug? **`npx genex list`** prints every game on your account —
|
|
132
|
+
slug, status, and page link for each — signing you in first if needed. Find the
|
|
133
|
+
game there, then `link` its slug.
|
|
134
|
+
|
|
131
135
|
`link` never creates a project: it signs in if needed (the browser opens once)
|
|
132
136
|
and rewrites the local link (source pushes authorize over HTTPS, so there's no
|
|
133
137
|
key to set up — it works from any machine).
|