@genex-ai/cli-demo 1.4.0-dev.373 → 1.4.1-dev.375
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/dist/index.js +169 -0
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -4890,6 +4890,157 @@ async function runMakeRemixable(opts) {
|
|
|
4890
4890
|
}
|
|
4891
4891
|
}
|
|
4892
4892
|
|
|
4893
|
+
// src/commands/domain.ts
|
|
4894
|
+
var SUBCOMMANDS = ["add", "list", "verify", "remove"];
|
|
4895
|
+
function statusWord(d) {
|
|
4896
|
+
if (d.status === "active") return c.green("live");
|
|
4897
|
+
if (d.status === "failed") return c.red("stopped");
|
|
4898
|
+
return c.dim("waiting for DNS");
|
|
4899
|
+
}
|
|
4900
|
+
async function runDomain(opts) {
|
|
4901
|
+
const log = createLogger({ quiet: opts.quiet });
|
|
4902
|
+
const sub = (opts.name ?? "list").trim();
|
|
4903
|
+
if (!SUBCOMMANDS.includes(sub)) {
|
|
4904
|
+
log.error(`Unknown subcommand \`${sub}\`. Use: ${SUBCOMMANDS.join(", ")}.`);
|
|
4905
|
+
process.exitCode = 1;
|
|
4906
|
+
return;
|
|
4907
|
+
}
|
|
4908
|
+
const needsHost = sub !== "list";
|
|
4909
|
+
const hostname = opts.hostname?.trim();
|
|
4910
|
+
if (needsHost && !hostname) {
|
|
4911
|
+
log.error(`\`genex domain ${sub}\` needs a hostname, e.g. ${c.cyan(`genex domain ${sub} play.yourdomain.com`)}.`);
|
|
4912
|
+
process.exitCode = 1;
|
|
4913
|
+
return;
|
|
4914
|
+
}
|
|
4915
|
+
const meta = await readProject();
|
|
4916
|
+
if (!meta?.id) {
|
|
4917
|
+
log.error("This folder isn't linked to a game.");
|
|
4918
|
+
log.dim(` Run ${c.cyan("genex link")} (or ${c.cyan("genex list")} to find the slug) first.`);
|
|
4919
|
+
process.exitCode = 1;
|
|
4920
|
+
return;
|
|
4921
|
+
}
|
|
4922
|
+
const apiUrl = getApiUrl(opts.apiUrl ?? meta.apiUrl);
|
|
4923
|
+
let token = opts.token ?? await readUserToken(opts.envPath);
|
|
4924
|
+
if (!token) {
|
|
4925
|
+
if (opts.noAuth) {
|
|
4926
|
+
log.error("Not signed in. Re-run without --no-auth to connect.");
|
|
4927
|
+
process.exitCode = 1;
|
|
4928
|
+
return;
|
|
4929
|
+
}
|
|
4930
|
+
log.plain("Not signed in \u2014 connecting\u2026");
|
|
4931
|
+
try {
|
|
4932
|
+
token = await authorize(apiUrl, getAuthUrl(opts.authUrl), {
|
|
4933
|
+
log,
|
|
4934
|
+
inlineWaitMs: opts.timeoutSec ? opts.timeoutSec * 1e3 : void 0
|
|
4935
|
+
});
|
|
4936
|
+
} catch (err) {
|
|
4937
|
+
if (err instanceof AuthPendingError) {
|
|
4938
|
+
printAuthHandoff(log, err);
|
|
4939
|
+
log.dim(`Then re-run ${c.cyan(`genex domain ${sub}`)}.`);
|
|
4940
|
+
return;
|
|
4941
|
+
}
|
|
4942
|
+
log.error(`Sign-in didn't complete: ${err instanceof Error ? err.message : String(err)}`);
|
|
4943
|
+
process.exitCode = 1;
|
|
4944
|
+
return;
|
|
4945
|
+
}
|
|
4946
|
+
await writeUserToken(token, opts.envPath);
|
|
4947
|
+
}
|
|
4948
|
+
const base = `${apiUrl}/api/projects/${encodeURIComponent(meta.id)}/domains`;
|
|
4949
|
+
const auth = { Authorization: `Bearer ${token}` };
|
|
4950
|
+
const call = (url, init2) => apiFetch(url, { ...init2, headers: { ...auth, "Content-Type": "application/json", ...init2?.headers ?? {} } });
|
|
4951
|
+
let res;
|
|
4952
|
+
try {
|
|
4953
|
+
if (sub === "list") res = await call(base);
|
|
4954
|
+
else if (sub === "add") res = await call(base, { method: "POST", body: JSON.stringify({ hostname }) });
|
|
4955
|
+
else if (sub === "verify")
|
|
4956
|
+
res = await call(`${base}/${encodeURIComponent(hostname)}/verify`, { method: "POST" });
|
|
4957
|
+
else res = await call(`${base}/${encodeURIComponent(hostname)}`, { method: "DELETE" });
|
|
4958
|
+
} catch (err) {
|
|
4959
|
+
log.error(`Couldn't reach the API at ${apiUrl}.`);
|
|
4960
|
+
log.dim(` ${String(err)}`);
|
|
4961
|
+
process.exitCode = 1;
|
|
4962
|
+
return;
|
|
4963
|
+
}
|
|
4964
|
+
if (!res.ok) {
|
|
4965
|
+
if (await printedStructuredError(res)) {
|
|
4966
|
+
process.exitCode = 1;
|
|
4967
|
+
return;
|
|
4968
|
+
}
|
|
4969
|
+
const body = await res.json().catch(() => null);
|
|
4970
|
+
if (res.status === 503) {
|
|
4971
|
+
log.error("Custom domains aren't available on this Genex environment yet.");
|
|
4972
|
+
} else if (res.status === 404) {
|
|
4973
|
+
log.error(sub === "list" || sub === "add" ? "That game wasn't found on this account." : `${hostname} isn't connected to this game.`);
|
|
4974
|
+
} else {
|
|
4975
|
+
log.error(body?.message ?? body?.error ?? `Request failed (${res.status}).`);
|
|
4976
|
+
}
|
|
4977
|
+
process.exitCode = 1;
|
|
4978
|
+
return;
|
|
4979
|
+
}
|
|
4980
|
+
const data = await res.json();
|
|
4981
|
+
if (opts.json) {
|
|
4982
|
+
log.plain(JSON.stringify(data, null, 2));
|
|
4983
|
+
return;
|
|
4984
|
+
}
|
|
4985
|
+
if (sub === "list") {
|
|
4986
|
+
const rows = data.domains ?? [];
|
|
4987
|
+
if (rows.length === 0) {
|
|
4988
|
+
log.plain("No domains connected to this game.");
|
|
4989
|
+
log.dim(` ${c.cyan("genex domain add play.yourdomain.com")} to connect one.`);
|
|
4990
|
+
return;
|
|
4991
|
+
}
|
|
4992
|
+
for (const d of rows) log.plain(` ${d.hostname.padEnd(34)} ${statusWord(d)}`);
|
|
4993
|
+
return;
|
|
4994
|
+
}
|
|
4995
|
+
if (sub === "add") {
|
|
4996
|
+
if (data.supported === false) {
|
|
4997
|
+
log.plain(String(data.message ?? "That domain's DNS host doesn't support one-click setup."));
|
|
4998
|
+
const manual = data.manual;
|
|
4999
|
+
const records = Array.isArray(manual?.records) ? manual.records : [];
|
|
5000
|
+
if (records.length > 0) {
|
|
5001
|
+
log.plain("");
|
|
5002
|
+
log.plain(" Add these two records at your DNS host:");
|
|
5003
|
+
log.plain("");
|
|
5004
|
+
for (const r of records) {
|
|
5005
|
+
log.plain(` ${c.cyan(String(r.type ?? ""))} ${String(r.name ?? "")}`);
|
|
5006
|
+
log.plain(` ${String(r.value ?? "")}`);
|
|
5007
|
+
}
|
|
5008
|
+
log.plain("");
|
|
5009
|
+
if (manual?.apexHint) {
|
|
5010
|
+
log.dim(" At a root domain your host may call the first one ALIAS or ANAME.");
|
|
5011
|
+
}
|
|
5012
|
+
log.dim(` Then run: genex domain verify ${String(data.hostname ?? "")}`);
|
|
5013
|
+
log.dim(" DNS can take a few minutes to spread.");
|
|
5014
|
+
return;
|
|
5015
|
+
}
|
|
5016
|
+
log.dim(" Your game stays reachable at its usual address.");
|
|
5017
|
+
return;
|
|
5018
|
+
}
|
|
5019
|
+
const applyUrl = String(data.applyUrl ?? "");
|
|
5020
|
+
const providerName = String(data.providerName ?? "your DNS host");
|
|
5021
|
+
log.success(`${data.hostname} can be connected through ${providerName}.`);
|
|
5022
|
+
log.plain("");
|
|
5023
|
+
log.plain(` Approve the DNS change here: ${c.cyan(applyUrl)}`);
|
|
5024
|
+
log.plain("");
|
|
5025
|
+
log.dim(" One click writes the records. Nothing to copy or paste.");
|
|
5026
|
+
if (!applyUrl.startsWith("https://")) {
|
|
5027
|
+
log.warn("That DNS host returned a setup link we could not verify \u2014 not opening it.");
|
|
5028
|
+
} else if (!opts.noOpen) {
|
|
5029
|
+
openBrowser(applyUrl, () => log.dim(" (Couldn't open a browser \u2014 use the link above.)"));
|
|
5030
|
+
}
|
|
5031
|
+
log.dim(` Then ${c.cyan(`genex domain verify ${String(data.hostname)}`)} once you have approved it.`);
|
|
5032
|
+
return;
|
|
5033
|
+
}
|
|
5034
|
+
if (sub === "verify") {
|
|
5035
|
+
const status = String(data.status ?? "");
|
|
5036
|
+
if (status === "active") log.success(`${hostname} is live.`);
|
|
5037
|
+
else if (status === "failed") log.error(`${hostname} is stopped \u2014 its verification record is missing.`);
|
|
5038
|
+
else log.plain(`${hostname} isn't verified yet \u2014 DNS changes can take a few minutes to spread.`);
|
|
5039
|
+
return;
|
|
5040
|
+
}
|
|
5041
|
+
log.success(`${hostname} disconnected.`);
|
|
5042
|
+
}
|
|
5043
|
+
|
|
4893
5044
|
// src/lib/promote.ts
|
|
4894
5045
|
async function promoteBuild(apiUrl, projectId, token, log) {
|
|
4895
5046
|
let res;
|
|
@@ -19018,6 +19169,10 @@ ${c.bold("Usage")}
|
|
|
19018
19169
|
genex publish [options] Build + push + make live, then list it in the gallery.
|
|
19019
19170
|
genex make-remixable [options] Make THIS game remixable by everyone \u2014 migrates a
|
|
19020
19171
|
private source onto a public managed genex repo.
|
|
19172
|
+
genex domain <sub> [host] Play this game on a domain you own:
|
|
19173
|
+
add | list | verify | remove. "add" opens a
|
|
19174
|
+
one-click approval at your DNS host \u2014 no
|
|
19175
|
+
records to copy or paste.
|
|
19021
19176
|
genex model "<prompt>" [options] Generate a 3D model (GLB); prints a public asset URL.
|
|
19022
19177
|
genex skybox "<prompt>" [options] Generate a skybox (equirect); prints a public asset URL.
|
|
19023
19178
|
genex sfx "<prompt>" [options] Generate a sound effect (mp3); prints a public asset URL.
|
|
@@ -19259,6 +19414,8 @@ ${c.bold("Examples")}
|
|
|
19259
19414
|
genex publish
|
|
19260
19415
|
genex publish --categories games,vfx
|
|
19261
19416
|
genex make-remixable
|
|
19417
|
+
genex domain add play.mygame.com
|
|
19418
|
+
genex domain list
|
|
19262
19419
|
genex publish --no-push --title "My Game"
|
|
19263
19420
|
genex model "weathered wooden barrel with iron bands"
|
|
19264
19421
|
genex skybox "golden hour over a misty mountain range"
|
|
@@ -19382,6 +19539,9 @@ function parseArgs(argv) {
|
|
|
19382
19539
|
case "--no-auth":
|
|
19383
19540
|
parsed.options.noAuth = true;
|
|
19384
19541
|
break;
|
|
19542
|
+
case "--no-open":
|
|
19543
|
+
parsed.options.noOpen = true;
|
|
19544
|
+
break;
|
|
19385
19545
|
case "--no-push":
|
|
19386
19546
|
parsed.options.noPush = true;
|
|
19387
19547
|
break;
|
|
@@ -19516,6 +19676,12 @@ function parseArgs(argv) {
|
|
|
19516
19676
|
} else {
|
|
19517
19677
|
(parsed.options.selectors ??= []).push(arg);
|
|
19518
19678
|
}
|
|
19679
|
+
} else if (parsed.command === "domain") {
|
|
19680
|
+
if (!parsed.options.hostname) parsed.options.hostname = arg;
|
|
19681
|
+
else {
|
|
19682
|
+
parsed.error = `Unexpected argument: ${arg}`;
|
|
19683
|
+
return parsed;
|
|
19684
|
+
}
|
|
19519
19685
|
} else if (parsed.command === "animations") {
|
|
19520
19686
|
parsed.options.query = parsed.options.query ? `${parsed.options.query} ${arg}` : arg;
|
|
19521
19687
|
} else {
|
|
@@ -19851,6 +20017,9 @@ async function main() {
|
|
|
19851
20017
|
case "make-remixable":
|
|
19852
20018
|
await runMakeRemixable(parsed.options);
|
|
19853
20019
|
break;
|
|
20020
|
+
case "domain":
|
|
20021
|
+
await runDomain(parsed.options);
|
|
20022
|
+
break;
|
|
19854
20023
|
case "controller":
|
|
19855
20024
|
await runController({ ...parsed.options, kind: parsed.options.name });
|
|
19856
20025
|
break;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@genex-ai/cli-demo",
|
|
3
|
-
"version": "1.4.
|
|
3
|
+
"version": "1.4.1-dev.375",
|
|
4
4
|
"description": "Set up your project's agent workspace (.claude/.codex/.cursor in the game folder), authorize, create a game project, generate AI assets, and publish (genex CLI).",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|