@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.
Files changed (2) hide show
  1. package/dist/index.js +169 -0
  2. 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.0-dev.373",
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": {