@genex-ai/cli-demo 0.48.0 → 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 +121 -1
- package/package.json +1 -1
- package/templates/skills/genex-ai-hud/SKILL.md +1 -1
- package/templates/skills/genex-ai-image/SKILL.md +1 -1
- package/templates/skills/genex-ai-menu/SKILL.md +1 -1
- package/templates/skills/genex-ai-model/SKILL.md +1 -1
- package/templates/skills/genex-ai-sfx/SKILL.md +1 -1
- package/templates/skills/genex-ai-skybox/SKILL.md +1 -1
- package/templates/skills/genex-ai-texture/SKILL.md +1 -1
- package/templates/skills/genex-ai-video/SKILL.md +1 -1
- package/templates/skills/genex-explore/SKILL.md +1 -1
- package/templates/skills/genex-getting-started/SKILL.md +6 -2
- package/templates/skills/genex-updates/SKILL.md +1 -1
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
|
@@ -8,7 +8,7 @@ import fs from "fs";
|
|
|
8
8
|
import os from "os";
|
|
9
9
|
import path from "path";
|
|
10
10
|
import { fileURLToPath } from "url";
|
|
11
|
-
var RAW_CHANNEL = "
|
|
11
|
+
var RAW_CHANNEL = "dev";
|
|
12
12
|
var CLI_CHANNEL = RAW_CHANNEL === "dev" ? "dev" : "latest";
|
|
13
13
|
var STANDS = {
|
|
14
14
|
prod: { api: "https://api.genex.games", dashboard: "https://genex.games" },
|
|
@@ -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
|
@@ -391,7 +391,7 @@ after a bad single costs the whole chain).
|
|
|
391
391
|
|
|
392
392
|
## Troubleshooting
|
|
393
393
|
|
|
394
|
-
- **"Not authorized"** — run `npx @genex-ai/cli-demo@
|
|
394
|
+
- **"Not authorized"** — run `npx @genex-ai/cli-demo@dev init` first (it
|
|
395
395
|
writes your `GENEX_TOKEN`).
|
|
396
396
|
- **"Prompt rejected"** — the provider's content-safety filter blocked the
|
|
397
397
|
prompt. Non-retryable; rewrite the wording.
|
|
@@ -182,7 +182,7 @@ set belongs to `$genex-ai-hud` — both build on this command.
|
|
|
182
182
|
|
|
183
183
|
## Troubleshooting
|
|
184
184
|
|
|
185
|
-
- **"Not authorized"** — run `npx @genex-ai/cli-demo@
|
|
185
|
+
- **"Not authorized"** — run `npx @genex-ai/cli-demo@dev init` first (it writes your `GENEX_TOKEN`).
|
|
186
186
|
- **"Prompt rejected"** — the provider's content-safety filter blocked the prompt.
|
|
187
187
|
This is non-retryable; retrying the same wording fails again. Rewrite the prompt.
|
|
188
188
|
- **Decal is an opaque rectangle** — the image has no alpha. Regenerate with
|
|
@@ -321,7 +321,7 @@ upgrades, in order of effort:
|
|
|
321
321
|
|
|
322
322
|
## Troubleshooting
|
|
323
323
|
|
|
324
|
-
- **"Not authorized"** — run `npx @genex-ai/cli-demo@
|
|
324
|
+
- **"Not authorized"** — run `npx @genex-ai/cli-demo@dev init` first (it
|
|
325
325
|
writes your `GENEX_TOKEN`).
|
|
326
326
|
- **"Prompt rejected"** — the provider's content-safety filter blocked the
|
|
327
327
|
prompt. Non-retryable; rewrite the wording.
|
|
@@ -107,7 +107,7 @@ scene is a ghost: players and objects pass straight through it.
|
|
|
107
107
|
|
|
108
108
|
## Troubleshooting
|
|
109
109
|
|
|
110
|
-
- **"Not authorized"** — run `npx @genex-ai/cli-demo@
|
|
110
|
+
- **"Not authorized"** — run `npx @genex-ai/cli-demo@dev init` first (it writes your `GENEX_TOKEN`).
|
|
111
111
|
- **"Out of credits" (`insufficient_credits`)** — the account has no credits left for
|
|
112
112
|
this model generation. Tell the user the facts the CLI printed: their balance, this
|
|
113
113
|
generation's cost, and when their credits refill. Then offer to continue the build
|
|
@@ -72,7 +72,7 @@ Reuse one loaded `buffer` across many plays; create a fresh `Audio`/`PositionalA
|
|
|
72
72
|
|
|
73
73
|
## Troubleshooting
|
|
74
74
|
|
|
75
|
-
- **"Not authorized"** — run `npx @genex-ai/cli-demo@
|
|
75
|
+
- **"Not authorized"** — run `npx @genex-ai/cli-demo@dev init` first (it writes your `GENEX_TOKEN`).
|
|
76
76
|
- **"Out of credits" (`insufficient_credits`)** — the account has no credits left for
|
|
77
77
|
this sound generation. Tell the user the facts the CLI printed: their balance, this
|
|
78
78
|
generation's cost, and when their credits refill. Then offer to continue the build
|
|
@@ -76,7 +76,7 @@ scene.background = texture; // keep the raw texture for the visible sky
|
|
|
76
76
|
|
|
77
77
|
## Troubleshooting
|
|
78
78
|
|
|
79
|
-
- **"Not authorized"** — run `npx @genex-ai/cli-demo@
|
|
79
|
+
- **"Not authorized"** — run `npx @genex-ai/cli-demo@dev init` first (it writes your `GENEX_TOKEN`).
|
|
80
80
|
- **"Out of credits" (`insufficient_credits`)** — the account has no credits left for
|
|
81
81
|
this skybox generation. Tell the user the facts the CLI printed: their balance, this
|
|
82
82
|
generation's cost, and when their credits refill. Then offer to continue the build
|
|
@@ -81,7 +81,7 @@ scene.add(ground);
|
|
|
81
81
|
|
|
82
82
|
## Troubleshooting
|
|
83
83
|
|
|
84
|
-
- **"Not authorized"** — run `npx @genex-ai/cli-demo@
|
|
84
|
+
- **"Not authorized"** — run `npx @genex-ai/cli-demo@dev init` first (it writes your `GENEX_TOKEN`).
|
|
85
85
|
- **"Out of credits" (`insufficient_credits`)** — the account has no credits left for
|
|
86
86
|
this texture generation. Tell the user the facts the CLI printed: their balance,
|
|
87
87
|
this generation's cost, and when their credits refill. Then offer to continue the
|
|
@@ -145,7 +145,7 @@ set belongs to `$genex-ai-hud` — both build on `npx genex image`/`video`.
|
|
|
145
145
|
|
|
146
146
|
## Troubleshooting
|
|
147
147
|
|
|
148
|
-
- **"Not authorized"** — run `npx @genex-ai/cli-demo@
|
|
148
|
+
- **"Not authorized"** — run `npx @genex-ai/cli-demo@dev init` first (it writes your `GENEX_TOKEN`).
|
|
149
149
|
- **"Prompt rejected"** — the provider's content-safety filter blocked the prompt.
|
|
150
150
|
This is non-retryable; retrying the same wording fails again. Rewrite the prompt.
|
|
151
151
|
- **Nothing plays / black surface** — the first `video.play()` must run inside a user
|
|
@@ -43,7 +43,7 @@ Add `--json` for machine-readable output.
|
|
|
43
43
|
**As a NEW game (start from the whole project):**
|
|
44
44
|
|
|
45
45
|
1. `git clone <clone URL from the output> <name>` — pick a short one-word name.
|
|
46
|
-
2. `cd <name>`, then `npx @genex-ai/cli-demo@
|
|
46
|
+
2. `cd <name>`, then `npx @genex-ai/cli-demo@dev init <name>` — never use
|
|
47
47
|
`--force`. This creates your own project; the original is untouched.
|
|
48
48
|
3. `npm install`, keep `base: './'` in `vite.config`, then build your changes
|
|
49
49
|
and ship with `npx genex preview`.
|
|
@@ -125,9 +125,13 @@ and re-link the clone to the same live game:
|
|
|
125
125
|
```bash
|
|
126
126
|
git clone <the game's repo url> my-game && cd my-game
|
|
127
127
|
npm install
|
|
128
|
-
npx @genex-ai/cli-demo@
|
|
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).
|
|
@@ -148,7 +152,7 @@ Safe to run any time — genex-owned skills are refreshed to the latest version,
|
|
|
148
152
|
and your own files are never touched:
|
|
149
153
|
|
|
150
154
|
```bash
|
|
151
|
-
npx @genex-ai/cli-demo@
|
|
155
|
+
npx @genex-ai/cli-demo@dev init
|
|
152
156
|
```
|
|
153
157
|
|
|
154
158
|
Use `--force` only if you intentionally want your own existing files overwritten
|
|
@@ -38,7 +38,7 @@ update, so update immediately.)
|
|
|
38
38
|
Run exactly the command the nudge printed, from the game project root:
|
|
39
39
|
|
|
40
40
|
```bash
|
|
41
|
-
npm i -D @genex-ai/cli-demo@
|
|
41
|
+
npm i -D @genex-ai/cli-demo@dev # the genex CLI (a dev dependency)
|
|
42
42
|
npm i @genex-ai/embed-sdk@latest # identity/saves SDK (ships inside the game)
|
|
43
43
|
npm i @genex-ai/multiplayer@latest # multiplayer SDK (only if the game uses it)
|
|
44
44
|
```
|