@genex-ai/cli-demo 1.36.19-dev.1076 → 1.36.20-dev.1078
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
CHANGED
|
@@ -10853,13 +10853,77 @@ async function ensureRealDir(dir, label) {
|
|
|
10853
10853
|
}
|
|
10854
10854
|
}
|
|
10855
10855
|
|
|
10856
|
+
// src/lib/web-version.ts
|
|
10857
|
+
var NATIVE_PAGE_WOULD_BE_REPLACED = "native_page_would_be_replaced";
|
|
10858
|
+
function isNativePageRefusal(body) {
|
|
10859
|
+
return body?.error === NATIVE_PAGE_WOULD_BE_REPLACED || body?.code === NATIVE_PAGE_WOULD_BE_REPLACED;
|
|
10860
|
+
}
|
|
10861
|
+
function explainNativePageRefusal(log, command, uploaded = false) {
|
|
10862
|
+
log.error("This game's page currently shows its Mac/Windows/Linux app \u2014 nothing was changed.");
|
|
10863
|
+
log.dim(" Making this web build live would replace that page: visitors would get this web");
|
|
10864
|
+
log.dim(" build instead of the app.");
|
|
10865
|
+
if (uploaded) log.dim(" The build was uploaded to the preview channel only; players never see it there.");
|
|
10866
|
+
log.dim(" Re-run with --replace-native-page ONLY if this folder really is the game's web version:");
|
|
10867
|
+
log.dim(` npx genex ${command} --replace-native-page`);
|
|
10868
|
+
}
|
|
10869
|
+
var REFUSALS = {
|
|
10870
|
+
no_native_build: [
|
|
10871
|
+
"This game has no Mac, Windows or Linux build players get, so removing its web version would leave nothing to play \u2014 nothing was changed.",
|
|
10872
|
+
" A native build waiting for review does not count yet.",
|
|
10873
|
+
" To take the game out of the gallery, unpublish it; to remove it entirely, delete it from the dashboard."
|
|
10874
|
+
],
|
|
10875
|
+
game_taken_down: [
|
|
10876
|
+
"This game was taken down by moderation; its build is kept for the review \u2014 nothing was changed."
|
|
10877
|
+
],
|
|
10878
|
+
web_removal_incomplete: [
|
|
10879
|
+
"The removal stopped partway: the web build no longer serves, but it is not fully gone yet.",
|
|
10880
|
+
" Run `npx genex web remove --yes` again to finish."
|
|
10881
|
+
],
|
|
10882
|
+
release_conflict: ["Another release of this game is in progress \u2014 nothing was changed. Try again in a moment."]
|
|
10883
|
+
};
|
|
10884
|
+
async function removeWebVersion(ctx, log) {
|
|
10885
|
+
let res;
|
|
10886
|
+
try {
|
|
10887
|
+
res = await apiFetch(`${ctx.apiUrl}/api/games/${ctx.projectId}/web`, {
|
|
10888
|
+
method: "DELETE",
|
|
10889
|
+
headers: { Authorization: `Bearer ${ctx.token}` }
|
|
10890
|
+
});
|
|
10891
|
+
} catch (err) {
|
|
10892
|
+
log.error(`Couldn't reach the API at ${ctx.apiUrl}: ${String(err)}`);
|
|
10893
|
+
return null;
|
|
10894
|
+
}
|
|
10895
|
+
const body = await res.json().catch(() => null);
|
|
10896
|
+
if (res.ok) return { removed: body?.removed === true, web: false };
|
|
10897
|
+
const words2 = body?.error ? REFUSALS[body.error] : void 0;
|
|
10898
|
+
if (words2 && body?.error) {
|
|
10899
|
+
const [first, ...rest] = words2;
|
|
10900
|
+
if (first) log.error(first);
|
|
10901
|
+
for (const line of rest) log.dim(line);
|
|
10902
|
+
return { refused: body.error };
|
|
10903
|
+
}
|
|
10904
|
+
if (res.status === 401) {
|
|
10905
|
+
log.error("Not authorized (HTTP 401) \u2014 your sign-in expired or was revoked. Run `genex link <your-game>`.");
|
|
10906
|
+
return null;
|
|
10907
|
+
}
|
|
10908
|
+
if (res.status === 404) {
|
|
10909
|
+
log.error("That game wasn't found on your account (HTTP 404), or this Genex server can't remove a web version yet.");
|
|
10910
|
+
return null;
|
|
10911
|
+
}
|
|
10912
|
+
log.error(`Couldn't remove the web version (HTTP ${res.status})${body?.error ? `: ${body.error}` : ""}.`);
|
|
10913
|
+
return null;
|
|
10914
|
+
}
|
|
10915
|
+
|
|
10856
10916
|
// src/lib/promote.ts
|
|
10857
|
-
async function promoteBuild(apiUrl, projectId, token, log) {
|
|
10917
|
+
async function promoteBuild(apiUrl, projectId, token, log, opts = {}) {
|
|
10858
10918
|
let res;
|
|
10859
10919
|
try {
|
|
10860
10920
|
res = await apiFetch(`${apiUrl}/api/games/${projectId}/promote`, {
|
|
10861
10921
|
method: "POST",
|
|
10862
|
-
headers: {
|
|
10922
|
+
headers: {
|
|
10923
|
+
Authorization: `Bearer ${token}`,
|
|
10924
|
+
...opts.replaceNativePage ? { "Content-Type": "application/json" } : {}
|
|
10925
|
+
},
|
|
10926
|
+
...opts.replaceNativePage ? { body: JSON.stringify({ replaceNativePage: true }) } : {}
|
|
10863
10927
|
});
|
|
10864
10928
|
} catch (err) {
|
|
10865
10929
|
log.error(`Couldn't reach the API at ${apiUrl}: ${String(err)}`);
|
|
@@ -10867,6 +10931,11 @@ async function promoteBuild(apiUrl, projectId, token, log) {
|
|
|
10867
10931
|
}
|
|
10868
10932
|
if (res.status === 409) {
|
|
10869
10933
|
const body2 = await res.json().catch(() => null);
|
|
10934
|
+
if (isNativePageRefusal(body2)) {
|
|
10935
|
+
const command = opts.command ?? "promote";
|
|
10936
|
+
explainNativePageRefusal(log, command, command === "publish");
|
|
10937
|
+
return null;
|
|
10938
|
+
}
|
|
10870
10939
|
if (body2?.code === "live_is_newer") {
|
|
10871
10940
|
log.error("The live build is newer than your preview \u2014 nothing was changed.");
|
|
10872
10941
|
log.dim(" This happens after the live version was restored, or after a later publish.");
|
|
@@ -10999,7 +11068,10 @@ async function runPublish(opts) {
|
|
|
10999
11068
|
return;
|
|
11000
11069
|
}
|
|
11001
11070
|
log.step("Making it live\u2026");
|
|
11002
|
-
if (!await promoteBuild(apiUrl, meta.id, token, log
|
|
11071
|
+
if (!await promoteBuild(apiUrl, meta.id, token, log, {
|
|
11072
|
+
replaceNativePage: opts.replaceNativePage,
|
|
11073
|
+
command: "publish"
|
|
11074
|
+
})) {
|
|
11003
11075
|
process.exitCode = 1;
|
|
11004
11076
|
return;
|
|
11005
11077
|
}
|
|
@@ -11026,6 +11098,7 @@ async function runPublish(opts) {
|
|
|
11026
11098
|
if (opts.license) body.license = opts.license;
|
|
11027
11099
|
if (opts.purchaseUrl) body.purchaseUrl = opts.purchaseUrl;
|
|
11028
11100
|
if (opts.productName) body.productName = opts.productName;
|
|
11101
|
+
if (opts.replaceNativePage) body.replaceNativePage = true;
|
|
11029
11102
|
body.matchmaking = detections.matchmaking ?? null;
|
|
11030
11103
|
body.gameStateUsed = detections.gameStateUsed;
|
|
11031
11104
|
body.mobileControls = detections.mobileControls;
|
|
@@ -11047,6 +11120,11 @@ async function runPublish(opts) {
|
|
|
11047
11120
|
const listedOnDisk = await readProject();
|
|
11048
11121
|
await writeProject({ ...listedOnDisk ?? meta, status: "published" });
|
|
11049
11122
|
const listed = await res.json().catch(() => null);
|
|
11123
|
+
if (listed?.project?.commitSha === null && listed.project.stagingCommitSha) {
|
|
11124
|
+
log.warn("Listed with its Mac/Windows/Linux app \u2014 your preview was not made live over the app's page.");
|
|
11125
|
+
log.dim(" Re-run with --replace-native-page ONLY if this folder really is the game's web version:");
|
|
11126
|
+
log.dim(" npx genex publish --no-push --replace-native-page");
|
|
11127
|
+
}
|
|
11050
11128
|
if (opts.regenerateCover && listed?.project?.coverAiEnabled === false) {
|
|
11051
11129
|
log.dim(" --regenerate-cover did nothing: this stand doesn't make AI covers.");
|
|
11052
11130
|
log.dim(" The cover is a real frame of the game \u2014 set one with `npx genex cover <file>`.");
|
|
@@ -11086,7 +11164,10 @@ async function runPromote(opts) {
|
|
|
11086
11164
|
return;
|
|
11087
11165
|
}
|
|
11088
11166
|
log.step("Making your previewed build live\u2026");
|
|
11089
|
-
const result = await promoteBuild(apiUrl, meta.id, token, log
|
|
11167
|
+
const result = await promoteBuild(apiUrl, meta.id, token, log, {
|
|
11168
|
+
replaceNativePage: opts.replaceNativePage,
|
|
11169
|
+
command: "promote"
|
|
11170
|
+
});
|
|
11090
11171
|
if (!result) {
|
|
11091
11172
|
process.exitCode = 1;
|
|
11092
11173
|
return;
|
|
@@ -30714,6 +30795,75 @@ async function runNative(opts) {
|
|
|
30714
30795
|
if (!await run5(opts, log)) process.exitCode = 1;
|
|
30715
30796
|
}
|
|
30716
30797
|
|
|
30798
|
+
// src/commands/web.ts
|
|
30799
|
+
function loggerFor3(opts) {
|
|
30800
|
+
if (!opts.json) return createLogger({ quiet: opts.quiet });
|
|
30801
|
+
const err = (s) => {
|
|
30802
|
+
if (!opts.quiet) writeSafely(process.stderr, `${s}
|
|
30803
|
+
`);
|
|
30804
|
+
};
|
|
30805
|
+
return {
|
|
30806
|
+
info: (m) => err(`i ${m}`),
|
|
30807
|
+
success: (m) => err(`\u2713 ${m}`),
|
|
30808
|
+
warn: (m) => err(`! ${m}`),
|
|
30809
|
+
error: (m) => writeSafely(process.stderr, `\u2717 ${m}
|
|
30810
|
+
`),
|
|
30811
|
+
step: (m) => err(`\u203A ${m}`),
|
|
30812
|
+
dim: err,
|
|
30813
|
+
plain: err
|
|
30814
|
+
};
|
|
30815
|
+
}
|
|
30816
|
+
async function runRemove2(opts, log) {
|
|
30817
|
+
if (inHostedSession()) {
|
|
30818
|
+
log.error("`genex web remove` is not available inside a Genex chat session \u2014 it changes what players get.");
|
|
30819
|
+
return false;
|
|
30820
|
+
}
|
|
30821
|
+
if (!opts.yes) {
|
|
30822
|
+
log.warn("This removes the game's web version and keeps its Mac/Windows/Linux apps.");
|
|
30823
|
+
log.dim(" The game's page goes back to showing the apps, and its play address stops serving a web build.");
|
|
30824
|
+
log.dim(" Both web builds go: the live one and the preview. They cannot be restored; publish again to bring one back.");
|
|
30825
|
+
log.dim(" The apps, the gallery listing, likes and the cover are not touched.");
|
|
30826
|
+
log.dim(" Refused when the game has no app players get \u2014 unpublish or delete such a game instead.");
|
|
30827
|
+
log.dim(` Re-run with --yes to do it: ${c.cyan("npx genex web remove --yes")}`);
|
|
30828
|
+
return false;
|
|
30829
|
+
}
|
|
30830
|
+
const meta = await readProject();
|
|
30831
|
+
if (!meta) {
|
|
30832
|
+
log.error("No genex project here. Run `genex link <game>` in this directory first.");
|
|
30833
|
+
return false;
|
|
30834
|
+
}
|
|
30835
|
+
const apiUrl = opts.apiUrl ?? getApiUrl(meta.apiUrl);
|
|
30836
|
+
const token = opts.token ?? await readUserToken(opts.envPath, apiUrl);
|
|
30837
|
+
if (!token) {
|
|
30838
|
+
log.error("Not authorized. Run `genex link <your-game>` to sign in.");
|
|
30839
|
+
return false;
|
|
30840
|
+
}
|
|
30841
|
+
log.step("Removing the web version\u2026");
|
|
30842
|
+
const result = await removeWebVersion({ apiUrl, projectId: meta.id, token }, log);
|
|
30843
|
+
if (!result) return false;
|
|
30844
|
+
if ("refused" in result) {
|
|
30845
|
+
if (opts.json) console.log(JSON.stringify({ error: result.refused }));
|
|
30846
|
+
return false;
|
|
30847
|
+
}
|
|
30848
|
+
if (opts.json) console.log(JSON.stringify(result));
|
|
30849
|
+
if (result.removed) log.success("Removed the web version. The game's page shows its apps again.");
|
|
30850
|
+
else log.success("This game has no web version on Genex \u2014 nothing to remove.");
|
|
30851
|
+
return true;
|
|
30852
|
+
}
|
|
30853
|
+
var SUBCOMMANDS3 = {
|
|
30854
|
+
remove: runRemove2
|
|
30855
|
+
};
|
|
30856
|
+
async function runWeb(opts) {
|
|
30857
|
+
const log = loggerFor3(opts);
|
|
30858
|
+
const run5 = opts.name ? SUBCOMMANDS3[opts.name] : void 0;
|
|
30859
|
+
if (!run5) {
|
|
30860
|
+
log.error("Unknown web command. Use `genex web remove [--yes] [--json]`.");
|
|
30861
|
+
process.exitCode = 1;
|
|
30862
|
+
return;
|
|
30863
|
+
}
|
|
30864
|
+
if (!await run5(opts, log)) process.exitCode = 1;
|
|
30865
|
+
}
|
|
30866
|
+
|
|
30717
30867
|
// src/lib/costs.ts
|
|
30718
30868
|
var TYPICAL_CREDITS = {
|
|
30719
30869
|
model: 35,
|
|
@@ -30826,6 +30976,9 @@ ${c.bold("Usage")}
|
|
|
30826
30976
|
genex promote [options] Make the previewed build live for players. No
|
|
30827
30977
|
rebuild \u2014 the exact build you previewed.
|
|
30828
30978
|
genex publish [options] Build + push + make live, then list it in the gallery.
|
|
30979
|
+
Both refuse to replace a page that shows only the
|
|
30980
|
+
game's Mac/Windows/Linux app unless given
|
|
30981
|
+
--replace-native-page.
|
|
30829
30982
|
genex accept Read the current Terms, Privacy and Content
|
|
30830
30983
|
policies and agree, so publishing is allowed.
|
|
30831
30984
|
Needs a real terminal: agreeing is a contract,
|
|
@@ -30838,6 +30991,9 @@ ${c.bold("Usage")}
|
|
|
30838
30991
|
"publish" takes a .zip, a .app or the build's
|
|
30839
30992
|
folder and makes it what desktop players install
|
|
30840
30993
|
for its platform and processor, live at once.
|
|
30994
|
+
genex web remove [--yes] [--json] Remove this game's web version and keep its
|
|
30995
|
+
Mac/Windows/Linux apps: the page shows the apps
|
|
30996
|
+
again. Refused for a game with no app.
|
|
30841
30997
|
genex cover [<file>] Show this game's cover and where it came from, or
|
|
30842
30998
|
make <file> (PNG/JPEG/WebP, \u22648 MB) the cover: one
|
|
30843
30999
|
real frame of the game \u2014 its signature moment,
|
|
@@ -31189,6 +31345,9 @@ ${c.bold("Options for `preview` / `publish`")}
|
|
|
31189
31345
|
--purchase-url <url> (operator) External purchase URL \u2014 marks the game a
|
|
31190
31346
|
paid-product demo ("Get \u2026" replaces Remix). Admin-only.
|
|
31191
31347
|
--product-name <name> (operator) Product name for the "Get {name}" button.
|
|
31348
|
+
--replace-native-page (publish, promote) Let this web build replace a page that
|
|
31349
|
+
shows only the game's Mac/Windows/Linux app. Use it ONLY when
|
|
31350
|
+
this folder really is the game's web version.
|
|
31192
31351
|
--api-url <url> (publish) Override the API base URL.
|
|
31193
31352
|
--env <path> Token env file (default: ~/.genex/env).
|
|
31194
31353
|
|
|
@@ -31676,6 +31835,24 @@ ${c.bold("Content filter")}
|
|
|
31676
31835
|
a body) as body horror \u2014 describe the object or machine instead: "a
|
|
31677
31836
|
statue-like figure threaded into the wall" passes where "cables entering her
|
|
31678
31837
|
spine" fails. Rejections are never retryable; reword and re-run.
|
|
31838
|
+
`,
|
|
31839
|
+
web: `${c.bold("genex web")} \u2014 this game's web version, beside its Mac/Windows/Linux apps.
|
|
31840
|
+
|
|
31841
|
+
${c.bold("Usage")}
|
|
31842
|
+
genex web remove Explain what removing the web version does, and stop.
|
|
31843
|
+
genex web remove --yes Remove it: both web builds (live and preview) go, and
|
|
31844
|
+
the game's page shows its apps again. The apps, the
|
|
31845
|
+
gallery listing, likes and the cover stay.
|
|
31846
|
+
--json One JSON object on stdout ({"removed": true, "web": false},
|
|
31847
|
+
or {"error": "<code>"} when refused).
|
|
31848
|
+
|
|
31849
|
+
${c.bold("Refused when")}
|
|
31850
|
+
The game has no app players get (a build waiting for review does not count): removing
|
|
31851
|
+
the web version would leave nothing to play \u2014 unpublish or delete the game instead.
|
|
31852
|
+
|
|
31853
|
+
${c.bold("Notes")}
|
|
31854
|
+
\`genex publish\` and \`genex promote\` never replace a page that shows only the apps
|
|
31855
|
+
unless given --replace-native-page.
|
|
31679
31856
|
`,
|
|
31680
31857
|
native: `${c.bold("genex native")} \u2014 a Unity, Godot or Unreal build players install from the Genex desktop app.
|
|
31681
31858
|
|
|
@@ -32032,6 +32209,12 @@ function parseArgs(argv) {
|
|
|
32032
32209
|
case "--gallery":
|
|
32033
32210
|
parsed.options.gallery = true;
|
|
32034
32211
|
break;
|
|
32212
|
+
// `genex publish|promote --replace-native-page`: the owner's word that this web
|
|
32213
|
+
// build may replace a page that shows only the game's apps. Its own flag, never
|
|
32214
|
+
// a reuse of --yes/--force, because the flag must say what it consents to.
|
|
32215
|
+
case "--replace-native-page":
|
|
32216
|
+
parsed.options.replaceNativePage = true;
|
|
32217
|
+
break;
|
|
32035
32218
|
// A pair rather than one flag taking a value: `--resellable false` reads
|
|
32036
32219
|
// as a filename to every shell user who has ever mistyped it, and the two
|
|
32037
32220
|
// states have to be distinguishable from ABSENT so `shop set` can leave
|
|
@@ -32855,6 +33038,9 @@ async function main() {
|
|
|
32855
33038
|
case "native":
|
|
32856
33039
|
await runNative(parsed.options);
|
|
32857
33040
|
break;
|
|
33041
|
+
case "web":
|
|
33042
|
+
await runWeb(parsed.options);
|
|
33043
|
+
break;
|
|
32858
33044
|
case "cover":
|
|
32859
33045
|
await runCover({ ...parsed.options, imagePath: parsed.options.name });
|
|
32860
33046
|
break;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@genex-ai/cli-demo",
|
|
3
|
-
"version": "1.36.
|
|
3
|
+
"version": "1.36.20-dev.1078",
|
|
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": {
|
|
@@ -104,5 +104,12 @@ Ask once per round of work, in one line, and keep working while you wait.
|
|
|
104
104
|
- **`preview` says the folder is behind another machine** (`stale_source`):
|
|
105
105
|
`npx genex pull` takes the newer draft, then redo the change — never
|
|
106
106
|
`--force` over somebody else's work.
|
|
107
|
+
- **`promote` or `publish` says the page shows the game's Mac/Windows/Linux
|
|
108
|
+
app** (`native_page_would_be_replaced`): the game ships as desktop apps, and
|
|
109
|
+
this web build would replace the page that offers them. Nothing changed. Ask
|
|
110
|
+
the user whether this folder really is the game's web version; only on a
|
|
111
|
+
clear yes, re-run the same command with `--replace-native-page`. A web
|
|
112
|
+
version that went up by mistake comes off with `npx genex web remove --yes`
|
|
113
|
+
(the apps stay).
|
|
107
114
|
- **Out of credits or a lane down**: `npx genex doctor` says which; publishing
|
|
108
115
|
itself costs nothing.
|