@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 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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@genex-ai/cli-demo",
3
- "version": "0.48.0-dev.91",
3
+ "version": "0.49.0-dev.92",
4
4
  "description": "Set up your ~/.claude workspace, authorize, create a game project, generate AI assets, and publish (genex CLI).",
5
5
  "type": "module",
6
6
  "bin": {
@@ -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).