@genex-ai/cli-demo 1.34.6-dev.713 → 1.34.6-dev.717

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 +172 -38
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -9046,7 +9046,7 @@ async function reportGlassTerminal(view, outDir, log, json) {
9046
9046
  process.exitCode = 1;
9047
9047
  }
9048
9048
  }
9049
- async function awaitAndReport(apiUrl, token, id, kind, log, open = false, json = false, local) {
9049
+ async function awaitAndReport(apiUrl, token, id, kind, log, open = false, json = false, local, bound) {
9050
9050
  if (!json) log.step(waitCopy(kind));
9051
9051
  const started = Date.now();
9052
9052
  const view = await waitForTerminalView(
@@ -9058,9 +9058,11 @@ async function awaitAndReport(apiUrl, token, id, kind, log, open = false, json =
9058
9058
  } : (p) => {
9059
9059
  const minutes = Math.floor((Date.now() - started) / 6e4);
9060
9060
  log.dim(` ${p}%${minutes >= 1 ? ` (${minutes} min)` : ""}`);
9061
- }
9061
+ },
9062
+ bound?.timeoutMs
9062
9063
  );
9063
9064
  if (!view) {
9065
+ if (bound) return bound.onExpiry();
9064
9066
  if (json) writeJson({ kind, id, status: "failed", error: "Timed out waiting for the generation." });
9065
9067
  else log.error("Timed out waiting for the generation.");
9066
9068
  process.exitCode = 1;
@@ -9088,11 +9090,18 @@ function waitCopy(kind) {
9088
9090
  }
9089
9091
  }
9090
9092
  async function waitForTerminalView(apiUrl, token, id, kind, onProgress = () => {
9091
- }) {
9092
- const timeoutMs = waitTimeoutFor(kind);
9093
+ }, boundMs) {
9094
+ const timeoutMs = boundMs ?? waitTimeoutFor(kind);
9093
9095
  const deadline = Date.now() + timeoutMs;
9094
9096
  const streamed = await waitViaSSE(apiUrl, token, id, onProgress, deadline);
9095
- return streamed === "unsupported" ? poll(apiUrl, token, id, onProgress, timeoutMs) : streamed;
9097
+ if (streamed !== "unsupported") return streamed;
9098
+ return poll(
9099
+ apiUrl,
9100
+ token,
9101
+ id,
9102
+ onProgress,
9103
+ boundMs === void 0 ? timeoutMs : Math.max(0, deadline - Date.now())
9104
+ );
9096
9105
  }
9097
9106
  async function reportTerminal(kind, view, log, open = false, json = false, local) {
9098
9107
  if (view.status !== "completed") {
@@ -9378,7 +9387,9 @@ async function poll(apiUrl, token, id, onProgress, timeoutMs) {
9378
9387
  }
9379
9388
  } catch {
9380
9389
  }
9381
- await sleep3(3e3);
9390
+ const remaining = deadline - Date.now();
9391
+ if (remaining <= 0) break;
9392
+ await sleep3(Math.min(3e3, remaining));
9382
9393
  }
9383
9394
  return null;
9384
9395
  }
@@ -9825,6 +9836,8 @@ async function runModelAnimate(opts) {
9825
9836
 
9826
9837
  // src/commands/wait.ts
9827
9838
  import path27 from "path";
9839
+ var MAX_TIMEOUT_SEC = 2e6;
9840
+ var STILL_RUNNING_EXIT = 2;
9828
9841
  var TERMINAL2 = /* @__PURE__ */ new Set(["completed", "failed"]);
9829
9842
  async function runWait(opts) {
9830
9843
  if (opts.all) return runWaitAll(opts);
@@ -9832,7 +9845,7 @@ async function runWait(opts) {
9832
9845
  const id = opts.id?.trim();
9833
9846
  if (!id) {
9834
9847
  log.error(
9835
- `Missing generation id. Usage: ${c.cyan("genex wait <id>")} \u2014 the id printed when you enqueued with --no-wait. For one status line per generation this project enqueued: ${c.cyan("genex wait --all")}.`
9848
+ `Missing generation id. Usage: ${c.cyan("genex wait <id>")} \u2014 the id printed when you enqueued with --no-wait. Add ${c.cyan("--status")} for one snapshot instead of a wait, or ${c.cyan("--timeout <seconds>")} to bound the wait. For one status line per generation this project enqueued: ${c.cyan("genex wait --all")}.`
9836
9849
  );
9837
9850
  process.exitCode = 1;
9838
9851
  return;
@@ -9885,6 +9898,29 @@ async function runWait(opts) {
9885
9898
  log.plain(c.bold("genex wait"));
9886
9899
  log.dim(` ${kind} ${id}`);
9887
9900
  log.plain("");
9901
+ const rowFor = (v) => viewRow(v, {
9902
+ id,
9903
+ kind,
9904
+ // A generation enqueued in some OTHER folder isn't in this ledger, so the
9905
+ // kind stands in for the prompt — the same substitution the local
9906
+ // delivery above makes.
9907
+ prompt: ledgerEntry?.prompt || kind,
9908
+ queuedAt: ledgerEntry?.queuedAt ?? ""
9909
+ });
9910
+ if (opts.status) {
9911
+ const row = rowFor(view);
9912
+ if (opts.json) {
9913
+ writeJson(row);
9914
+ } else {
9915
+ printStatusRow(row, log);
9916
+ log.plain("");
9917
+ log.dim(
9918
+ view.status === "failed" ? ` It failed \u2014 there is nothing to collect. See why with: ${c.cyan(`npx genex wait ${id}`)}` : TERMINAL2.has(view.status) ? ` Pick the asset up (URLs, and the files in a Genex Tools workspace) with: ${c.cyan(`npx genex wait ${id}`)}` : ` Still running server-side. Block until it lands with ${c.cyan(`npx genex wait ${id}`)}, or bound that with ${c.cyan("--timeout <seconds>")}.`
9919
+ );
9920
+ }
9921
+ if (!TERMINAL2.has(view.status)) process.exitCode = STILL_RUNNING_EXIT;
9922
+ return;
9923
+ }
9888
9924
  if (TERMINAL2.has(view.status)) {
9889
9925
  await reportTerminal(kind, view, log, opts.open, opts.json, local);
9890
9926
  return;
@@ -9898,8 +9934,51 @@ async function runWait(opts) {
9898
9934
  ` \u23F3 Motion batches take several minutes server-side (each clip is its own job; a 16-clip walk + run set ten or more) \u2014 you don't have to sit here. Keep building and check back with ${c.cyan("genex wait --all")}.`
9899
9935
  );
9900
9936
  }
9937
+ if (opts.timeoutSec !== void 0) {
9938
+ const seconds = Math.min(opts.timeoutSec, MAX_TIMEOUT_SEC);
9939
+ const startedAt = Date.now();
9940
+ await awaitAndReport(apiUrl, token, id, kind, log, opts.open, opts.json, local, {
9941
+ timeoutMs: seconds * 1e3,
9942
+ onExpiry: () => reportStillRunning(apiUrl, token, id, kind, view, rowFor, log, opts, local, seconds, startedAt)
9943
+ });
9944
+ return;
9945
+ }
9901
9946
  await awaitAndReport(apiUrl, token, id, kind, log, opts.open, opts.json, local);
9902
9947
  }
9948
+ async function reportStillRunning(apiUrl, token, id, kind, lastKnown, rowFor, log, opts, local, seconds, startedAt) {
9949
+ const view = await peekGeneration(apiUrl, token, id) ?? lastKnown;
9950
+ if (TERMINAL2.has(view.status)) {
9951
+ await reportTerminal(kind, view, log, opts.open, opts.json, local);
9952
+ return;
9953
+ }
9954
+ const row = rowFor(view);
9955
+ if (opts.json) {
9956
+ writeJson({ ...row, timedOut: true });
9957
+ } else {
9958
+ printStatusRow(row, log);
9959
+ log.plain("");
9960
+ const waited = Math.max(0, Date.now() - startedAt) / 1e3;
9961
+ const shown = waited < 10 ? waited.toFixed(1) : Math.round(waited).toString();
9962
+ log.warn(
9963
+ `Stopped waiting after ${shown}s (ceiling ${seconds}s) \u2014 ${c.bold("the generation is STILL RUNNING")} server-side. Nothing was cancelled, and nothing extra was billed.`
9964
+ );
9965
+ log.dim(` Resume waiting: ${c.cyan(`npx genex wait ${id}`)}`);
9966
+ log.dim(` One snapshot, no waiting: ${c.cyan(`npx genex wait ${id} --status`)}`);
9967
+ }
9968
+ process.exitCode = STILL_RUNNING_EXIT;
9969
+ }
9970
+ async function peekGeneration(apiUrl, token, id) {
9971
+ try {
9972
+ const res = await apiFetch(`${apiUrl}/api/generations/${id}`, {
9973
+ headers: { Authorization: `Bearer ${token}` }
9974
+ });
9975
+ if (!res.ok) return null;
9976
+ const { generation } = await res.json();
9977
+ return generation;
9978
+ } catch {
9979
+ return null;
9980
+ }
9981
+ }
9903
9982
  async function runWaitAll(opts) {
9904
9983
  const log = createLogger({ quiet: opts.quiet || opts.json });
9905
9984
  const cwd = opts.cwd ?? process.cwd();
@@ -9980,20 +10059,7 @@ async function runWaitAll(opts) {
9980
10059
  }
9981
10060
  log.plain(c.bold("genex wait --all"));
9982
10061
  log.plain("");
9983
- for (const r of rows) {
9984
- const mark = r.partial ? c.yellow("\u26A0") : r.status === "done" ? c.green("\u2713") : r.status === "failed" ? c.red("\u2717") : c.yellow("\u2026");
9985
- const label = r.status === "running" && typeof r.progress === "number" ? `running ${r.progress}%` : r.partial ? "partial" : r.status;
9986
- log.plain(` ${mark} ${label.padEnd(12)} ${r.kind.padEnd(8)} ${r.id} ${c.dim(`"${r.prompt.slice(0, 56)}"`)}`);
9987
- if (r.partial) {
9988
- log.plain(
9989
- ` ${c.yellow(`${r.partial.installed} of ${r.partial.requested} clips landed`)} \u2014 missing: ${r.partial.failed.join(", ")}. ${c.dim(`genex wait ${r.id}`)} names the fix.`
9990
- );
9991
- }
9992
- if (r.status === "failed" && r.error) {
9993
- for (const line of describeGenerationFailure(r.kind, r.error).lines) log.plain(` ${line}`);
9994
- }
9995
- if (r.status === "done" && r.mocked) log.dim(` ${MOCKED_LINE}`);
9996
- }
10062
+ for (const r of rows) printStatusRow(r, log);
9997
10063
  const count = (s) => rows.filter((r) => r.status === s).length;
9998
10064
  const partial = rows.filter((r) => r.partial).length;
9999
10065
  const billingFailed = rows.filter(
@@ -10037,6 +10103,39 @@ async function runWaitAll(opts) {
10037
10103
  mode === "tools" || mode === "remix" ? " \u{1F4DC} Resumed or compacted? Re-read AGENTS.md before building on." : " \u{1F4DC} Resumed or compacted? Re-read AGENTS.md + DESIGN.md (continue from its Now: line) before building on."
10038
10104
  );
10039
10105
  }
10106
+ function printStatusRow(r, log) {
10107
+ const mark = r.partial ? c.yellow("\u26A0") : r.status === "done" ? c.green("\u2713") : r.status === "failed" ? c.red("\u2717") : c.yellow("\u2026");
10108
+ const label = r.status === "running" && typeof r.progress === "number" ? `running ${r.progress}%` : r.partial ? "partial" : r.status;
10109
+ log.plain(` ${mark} ${label.padEnd(12)} ${r.kind.padEnd(8)} ${r.id} ${c.dim(`"${r.prompt.slice(0, 56)}"`)}`);
10110
+ if (r.partial) {
10111
+ log.plain(
10112
+ ` ${c.yellow(`${r.partial.installed} of ${r.partial.requested} clips landed`)} \u2014 missing: ${r.partial.failed.join(", ")}. ${c.dim(`genex wait ${r.id}`)} names the fix.`
10113
+ );
10114
+ }
10115
+ if (r.status === "failed" && r.error) {
10116
+ for (const line of describeGenerationFailure(r.kind, r.error).lines) log.plain(` ${line}`);
10117
+ }
10118
+ if (r.status === "done" && r.mocked) log.dim(` ${MOCKED_LINE}`);
10119
+ }
10120
+ function viewRow(v, base) {
10121
+ switch (v.status) {
10122
+ case "completed": {
10123
+ const batch = motionBatchOutcome(v);
10124
+ return {
10125
+ ...base,
10126
+ status: "done",
10127
+ ...v.mocked === true ? { mocked: true } : {},
10128
+ ...batch?.partial ? { partial: { installed: batch.installed, requested: batch.requested, failed: batch.failures.map((f) => f.clip) } } : {}
10129
+ };
10130
+ }
10131
+ case "failed":
10132
+ return { ...base, status: "failed", error: v.error };
10133
+ case "processing":
10134
+ return { ...base, status: "running", progress: v.progress };
10135
+ default:
10136
+ return { ...base, status: "queued" };
10137
+ }
10138
+ }
10040
10139
  async function toRow(e, v, cwd) {
10041
10140
  const base = { id: e.id, kind: e.kind, prompt: e.prompt, queuedAt: e.queuedAt };
10042
10141
  if (v) {
@@ -10049,23 +10148,7 @@ async function toRow(e, v, cwd) {
10049
10148
  ).catch(() => {
10050
10149
  });
10051
10150
  }
10052
- switch (v.status) {
10053
- case "completed": {
10054
- const batch = motionBatchOutcome(v);
10055
- return {
10056
- ...base,
10057
- status: "done",
10058
- ...v.mocked === true ? { mocked: true } : {},
10059
- ...batch?.partial ? { partial: { installed: batch.installed, requested: batch.requested, failed: batch.failures.map((f) => f.clip) } } : {}
10060
- };
10061
- }
10062
- case "failed":
10063
- return { ...base, status: "failed", error: v.error };
10064
- case "processing":
10065
- return { ...base, status: "running", progress: v.progress };
10066
- default:
10067
- return { ...base, status: "queued" };
10068
- }
10151
+ return viewRow(v, base);
10069
10152
  }
10070
10153
  return { ...base, status: e.status === "completed" ? "done" : e.status === "failed" ? "failed" : "queued" };
10071
10154
  }
@@ -23931,6 +24014,12 @@ ${c.bold("Usage")}
23931
24014
  genex wait <id> Attach to a generation enqueued with --no-wait and
23932
24015
  print its asset URL(s) when it finishes. Safe to
23933
24016
  re-run \u2014 it never creates a new generation.
24017
+ --timeout <seconds> stops waiting at that ceiling:
24018
+ the generation keeps running, and you get its
24019
+ current row, the resume command, and exit 2.
24020
+ --status takes ONE snapshot instead of waiting at
24021
+ all \u2014 exit 0 if it finished, 2 if it is still going.
24022
+ Full page: genex wait --help.
23934
24023
  genex wait --all One status line per generation this project has
23935
24024
  enqueued (done/running/queued/failed) \u2014 a single
23936
24025
  API refresh, never blocks, never bills.
@@ -24298,6 +24387,8 @@ ${c.bold("Workflow")}
24298
24387
  --user-approved raises it (player's say-so first).
24299
24388
  genex wait <id> Pick up a generation enqueued with --no-wait.
24300
24389
  Never bills \u2014 re-running a generate command does.
24390
+ --timeout <seconds> bounds the wait (it keeps
24391
+ running; exit 2); --status is one snapshot and out.
24301
24392
  genex wait --all One status line per generation from this folder.
24302
24393
  genex auth [--force] Connect this machine (or switch accounts).
24303
24394
  genex tools Re-run setup here (refresh skills + rules).
@@ -24486,6 +24577,46 @@ ${c.bold("Content filter")}
24486
24577
  a body) as body horror \u2014 describe the object or machine instead: "a
24487
24578
  statue-like figure threaded into the wall" passes where "cables entering her
24488
24579
  spine" fails. Rejections are never retryable; reword and re-run.
24580
+ `,
24581
+ wait: `${c.bold("genex wait")} \u2014 attach to a generation already running, or just ask how it is doing.
24582
+
24583
+ ${c.bold("Usage")}
24584
+ genex wait <id> Block until it finishes, then print its asset URL(s)
24585
+ (and save the files, in a Genex Tools workspace).
24586
+ Never creates and never bills a generation \u2014 safe
24587
+ to re-run.
24588
+ genex wait <id> --timeout <secs> The same wait, with a ceiling. On expiry it prints
24589
+ the generation's current row, says it is STILL
24590
+ RUNNING server-side, prints the command that resumes
24591
+ the wait, and exits 2. Nothing is cancelled.
24592
+ genex wait <id> --status ONE snapshot and out \u2014 no polling, no sleeping.
24593
+ Prints the same row. Exits 0 if the generation is
24594
+ terminal (completed OR failed), 2 if it is still
24595
+ running. Reach for this instead of hand-polling.
24596
+ genex wait --all One status line per generation this folder enqueued
24597
+ \u2014 a single API refresh, never blocks, never bills.
24598
+
24599
+ ${c.bold("Exit codes")}
24600
+ 0 finished \u2014 or, with --status, terminal (completed OR failed)
24601
+ 1 something went wrong: unknown id, not authorized, unreachable API, or (without
24602
+ --status) the generation itself failed
24603
+ 2 still running \u2014 you stopped waiting, or you only asked
24604
+
24605
+ ${c.bold("Flags")}
24606
+ --status Wins over --timeout: it is already a zero-wait mode, so the two
24607
+ together is not an error, it is --status. With --all it changes
24608
+ nothing \u2014 that surface never blocks either way.
24609
+ --timeout <seconds> Bounds a blocking wait only. Ignored by --status and by --all.
24610
+ --json One machine-readable object: the status row for --status and for a
24611
+ --timeout expiry (which adds "timedOut": true), the terminal
24612
+ result otherwise.
24613
+ --open Open the finished asset in your browser.
24614
+ --out-dir <dir> (Genex Tools) where the saved files land. --no-download: URL only.
24615
+
24616
+ ${c.bold("Why --status exists")}
24617
+ Without a bound this command blocks for the per-kind ceiling \u2014 up to 30 minutes on a
24618
+ character stage. Callers hand-polled around that, and hand-polling loses every line
24619
+ the wait path prints. Ask once, act on the answer, keep building.
24489
24620
  `
24490
24621
  };
24491
24622
  function parseArgs(argv) {
@@ -24740,6 +24871,9 @@ function parseArgs(argv) {
24740
24871
  case "--all":
24741
24872
  parsed.options.all = true;
24742
24873
  break;
24874
+ case "--status":
24875
+ parsed.options.status = true;
24876
+ break;
24743
24877
  default: {
24744
24878
  if (needsValue.has(arg)) {
24745
24879
  const value = argv[i++];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@genex-ai/cli-demo",
3
- "version": "1.34.6-dev.713",
3
+ "version": "1.34.6-dev.717",
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": {