@genex-ai/cli-demo 1.36.2 → 1.36.3-dev.784

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.
@@ -6,11 +6,12 @@ import {
6
6
  blenderSetupHint,
7
7
  isRenderMode,
8
8
  sceneSummary,
9
- sheetOf
10
- } from "./chunk-GOP5FRSJ.js";
9
+ sheetOf,
10
+ writeSafely
11
+ } from "./chunk-EABIVAGT.js";
11
12
  import {
12
13
  getCliVersion
13
- } from "./chunk-QI7FIYBY.js";
14
+ } from "./chunk-K4EHXOZK.js";
14
15
 
15
16
  // src/commands/blender-mcp.ts
16
17
  import fs from "fs/promises";
@@ -145,7 +146,7 @@ ${r.error.trimEnd()}`);
145
146
  }
146
147
  ];
147
148
  function write(msg) {
148
- process.stdout.write(`${JSON.stringify(msg)}
149
+ writeSafely(process.stdout, `${JSON.stringify(msg)}
149
150
  `);
150
151
  }
151
152
  function reply(id, result) {
@@ -192,11 +193,11 @@ async function handle(msg) {
192
193
  fail(id, -32601, `Method not found: ${method}`);
193
194
  }
194
195
  var stderrLog = {
195
- step: (m) => process.stderr.write(`[genex-blender-mcp] ${m}
196
+ step: (m) => writeSafely(process.stderr, `[genex-blender-mcp] ${m}
196
197
  `),
197
- warn: (m) => process.stderr.write(`[genex-blender-mcp] ${m}
198
+ warn: (m) => writeSafely(process.stderr, `[genex-blender-mcp] ${m}
198
199
  `),
199
- error: (m) => process.stderr.write(`[genex-blender-mcp] ${m}
200
+ error: (m) => writeSafely(process.stderr, `[genex-blender-mcp] ${m}
200
201
  `)
201
202
  };
202
203
  async function mcpEndpoint() {
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  c,
3
3
  getTemplatesDir
4
- } from "./chunk-QI7FIYBY.js";
4
+ } from "./chunk-K4EHXOZK.js";
5
5
 
6
6
  // src/lib/blender-serve.ts
7
7
  import { spawn } from "child_process";
@@ -10,7 +10,189 @@ import {
10
10
  getGenexEnvPath,
11
11
  normalizeApiOrigin,
12
12
  originKey
13
- } from "./chunk-QI7FIYBY.js";
13
+ } from "./chunk-K4EHXOZK.js";
14
+
15
+ // src/lib/printed.ts
16
+ var printed = /* @__PURE__ */ new WeakSet();
17
+ function markPrinted(err) {
18
+ if (typeof err === "object" && err !== null) printed.add(err);
19
+ return err;
20
+ }
21
+ function wasPrinted(err) {
22
+ return typeof err === "object" && err !== null && printed.has(err);
23
+ }
24
+ var expected = /* @__PURE__ */ new WeakSet();
25
+ function markExpected(err) {
26
+ if (typeof err === "object" && err !== null) expected.add(err);
27
+ return err;
28
+ }
29
+ function isExpected(err) {
30
+ return typeof err === "object" && err !== null && expected.has(err);
31
+ }
32
+
33
+ // src/lib/error-kinds.ts
34
+ function asErrorLike(value) {
35
+ return typeof value === "object" && value !== null ? value : null;
36
+ }
37
+ var NETWORK_ERROR_CODES = /* @__PURE__ */ new Set([
38
+ "ECONNREFUSED",
39
+ "ECONNRESET",
40
+ "ECONNABORTED",
41
+ "ETIMEDOUT",
42
+ "ENOTFOUND",
43
+ "EAI_AGAIN",
44
+ "ENETUNREACH",
45
+ "ENETDOWN",
46
+ "EHOSTUNREACH",
47
+ "EHOSTDOWN",
48
+ "EPIPE"
49
+ ]);
50
+ function isNetworkCode(code) {
51
+ return typeof code === "string" && (NETWORK_ERROR_CODES.has(code) || code.startsWith("UND_ERR_"));
52
+ }
53
+ function networkErrorCode(err, depth = 0) {
54
+ const e = asErrorLike(err);
55
+ if (!e || depth > 4) return null;
56
+ if (isNetworkCode(e.code)) return e.code;
57
+ if (Array.isArray(e.errors)) {
58
+ for (const inner of e.errors) {
59
+ const code = networkErrorCode(inner, depth + 1);
60
+ if (code) return code;
61
+ }
62
+ }
63
+ return networkErrorCode(e.cause, depth + 1);
64
+ }
65
+ function isNetworkFetchFailure(err) {
66
+ const e = asErrorLike(err);
67
+ return e !== null && e.message === "fetch failed" && networkErrorCode(e.cause) !== null;
68
+ }
69
+ function networkFailureMessage(code) {
70
+ return `Couldn't reach Genex (${code}) \u2014 check your connection and re-run this command.`;
71
+ }
72
+ var FS_PERMISSION_CODES = /* @__PURE__ */ new Set(["EPERM", "EACCES", "EROFS"]);
73
+ var FS_SYSCALLS = /* @__PURE__ */ new Set([
74
+ "access",
75
+ "chmod",
76
+ "chown",
77
+ "close",
78
+ "copyfile",
79
+ "cp",
80
+ "fchmod",
81
+ "fchown",
82
+ "fdatasync",
83
+ "fsync",
84
+ "ftruncate",
85
+ "futime",
86
+ "lchown",
87
+ "link",
88
+ "lstat",
89
+ "lutimes",
90
+ "mkdir",
91
+ "mkdtemp",
92
+ "open",
93
+ "opendir",
94
+ "read",
95
+ "readlink",
96
+ "realpath",
97
+ "rename",
98
+ "rm",
99
+ "rmdir",
100
+ "scandir",
101
+ "stat",
102
+ "symlink",
103
+ "truncate",
104
+ "unlink",
105
+ "utime",
106
+ "write",
107
+ "writev"
108
+ ]);
109
+ function fsPermissionInfo(err) {
110
+ const e = asErrorLike(err);
111
+ if (!e || typeof e.code !== "string" || !FS_PERMISSION_CODES.has(e.code)) return null;
112
+ if (typeof e.syscall !== "string" || !FS_SYSCALLS.has(e.syscall)) return null;
113
+ const where = typeof e.dest === "string" ? e.dest : typeof e.path === "string" ? e.path : void 0;
114
+ return { code: e.code, syscall: e.syscall, ...where ? { path: where } : {} };
115
+ }
116
+ function fsPermissionAdvice(code, platform) {
117
+ if (code === "EROFS") return "it is on a read-only volume; run genex from a folder on a writable disk.";
118
+ if (platform === "win32") {
119
+ return "Windows refused; close any program that has it open and make sure your account may write there (antivirus or Controlled Folder Access can block it too), then re-run.";
120
+ }
121
+ if (platform === "darwin" && code === "EPERM") {
122
+ return "macOS blocked it; give your terminal or agent app access to this folder (System Settings \u2192 Privacy & Security \u2192 Files and Folders, or Full Disk Access) or allow the path in your agent's sandbox, then re-run.";
123
+ }
124
+ return "permission denied; make sure your user owns it (an earlier `sudo` run can leave it owned by root) and that your agent's sandbox, if any, allows writing there, then re-run.";
125
+ }
126
+ var READ_SYSCALLS = /* @__PURE__ */ new Set([
127
+ "access",
128
+ "lstat",
129
+ "opendir",
130
+ "read",
131
+ "readlink",
132
+ "realpath",
133
+ "scandir",
134
+ "stat"
135
+ ]);
136
+ function fsPermissionMessage(info, target, platform = process.platform) {
137
+ const where = target ?? info.path ?? "a file it needs";
138
+ const verb = target !== void 0 ? "write" : READ_SYSCALLS.has(info.syscall) ? "read" : info.syscall === "open" ? "open" : "write";
139
+ return `Genex couldn't ${verb} ${where} (${info.code}) \u2014 ${fsPermissionAdvice(info.code, platform)}`;
140
+ }
141
+ var FsPermissionError = class extends Error {
142
+ code;
143
+ syscall;
144
+ path;
145
+ constructor(info, message, cause) {
146
+ super(message, { cause });
147
+ this.name = "FsPermissionError";
148
+ this.code = info.code;
149
+ this.syscall = info.syscall;
150
+ if (info.path) this.path = info.path;
151
+ }
152
+ };
153
+ function explainFsPermission(err, target) {
154
+ const info = fsPermissionInfo(err);
155
+ if (!info) return null;
156
+ return markExpected(new FsPermissionError(info, fsPermissionMessage(info, target), err));
157
+ }
158
+ function describeError(err, platform = process.platform) {
159
+ if (isNetworkFetchFailure(err)) return networkFailureMessage(networkErrorCode(err) ?? "network");
160
+ if (!(err instanceof FsPermissionError)) {
161
+ const info = fsPermissionInfo(err);
162
+ if (info) return fsPermissionMessage(info, void 0, platform);
163
+ }
164
+ return err instanceof Error ? err.message : String(err);
165
+ }
166
+
167
+ // src/utils/stdio.ts
168
+ var broken = /* @__PURE__ */ new WeakSet();
169
+ function isBrokenPipe(err) {
170
+ if (typeof err !== "object" || err === null) return false;
171
+ const { code, message } = err;
172
+ if (code === "EPIPE") return true;
173
+ return typeof message === "string" && /\bEPIPE\b/.test(message);
174
+ }
175
+ function writeSafely(stream, text) {
176
+ if (broken.has(stream)) return;
177
+ try {
178
+ stream.write(text);
179
+ } catch (err) {
180
+ if (!isBrokenPipe(err)) throw err;
181
+ broken.add(stream);
182
+ }
183
+ }
184
+ function guardStdio(streams = [process.stdout, process.stderr]) {
185
+ for (const stream of streams) {
186
+ stream.on("error", (err) => {
187
+ const code = err?.code;
188
+ if (isBrokenPipe(err) || code === "ERR_STREAM_DESTROYED") {
189
+ broken.add(stream);
190
+ return;
191
+ }
192
+ throw err;
193
+ });
194
+ }
195
+ }
14
196
 
15
197
  // src/lib/terms.ts
16
198
  import readline from "readline";
@@ -310,7 +492,7 @@ async function runAccept(opts) {
310
492
 
311
493
  // src/lib/api.ts
312
494
  var stderrLogger = /* @__PURE__ */ (() => {
313
- const err = (s) => void process.stderr.write(s + "\n");
495
+ const err = (s) => writeSafely(process.stderr, s + "\n");
314
496
  return {
315
497
  info: err,
316
498
  success: (m) => err(`${c.green("\u2713")} ${m}`),
@@ -360,7 +542,7 @@ function shortDate(iso) {
360
542
  return d.toLocaleDateString("en-US", { month: "short", day: "numeric" });
361
543
  }
362
544
  function formatInsufficientCredits(body) {
363
- const message = body.message ?? `this generation costs ${body.price ?? "?"} credits; your balance is ${body.balance ?? 0}.`;
545
+ const message = body.message ?? (body.price === void 0 && typeof body.requiredCredits === "number" ? `this call reserves up to ${body.requiredCredits} credits; ${body.spendableCredits ?? 0} are spendable.` : `this generation costs ${body.price ?? "?"} credits; your balance is ${body.balance ?? 0}.`);
364
546
  const lines = [`${c.red("\u2717")} Out of credits \u2014 ${lowerFirst(message)}`];
365
547
  if (body.refillAt && body.refillTo) {
366
548
  lines.push(` Credits refill to ${body.refillTo} on ${shortDate(body.refillAt)}.`);
@@ -404,7 +586,13 @@ async function apiFetch(url, init = {}, opts = {}) {
404
586
  }
405
587
  const bearer = headers.get("authorization")?.replace(/^Bearer\s+/i, "");
406
588
  if (bearer) assertTokenDestination(bearer, url);
407
- const res = await fetch(url, { ...init, headers, redirect: "error" });
589
+ let res;
590
+ try {
591
+ res = await fetch(url, { ...init, headers, redirect: "error" });
592
+ } catch (err) {
593
+ if (networkErrorCode(err) !== null) markExpected(err);
594
+ throw err;
595
+ }
408
596
  if (res.status === 403 && !opts.noTermsWait && await isTermsRefusal(res)) {
409
597
  const log = stderrLogger;
410
598
  reportTermsRefusal(log, await termsRefusalUrl(res));
@@ -539,15 +727,19 @@ async function restrictFilePermissions(filePath) {
539
727
 
540
728
  // src/lib/secret-file.ts
541
729
  async function writeSecretFile(file, content, exclusive = false) {
542
- await fs3.mkdir(path3.dirname(file), { recursive: true, mode: 448 });
543
- const temporary = `${file}.${randomUUID()}.tmp`;
544
730
  try {
545
- await fs3.writeFile(temporary, content, { mode: 384, flag: "wx" });
546
- await restrictFilePermissions(temporary);
547
- if (exclusive) await fs3.link(temporary, file);
548
- else await fs3.rename(temporary, file);
549
- } finally {
550
- await fs3.rm(temporary, { force: true });
731
+ await fs3.mkdir(path3.dirname(file), { recursive: true, mode: 448 });
732
+ const temporary = `${file}.${randomUUID()}.tmp`;
733
+ try {
734
+ await fs3.writeFile(temporary, content, { mode: 384, flag: "wx" });
735
+ await restrictFilePermissions(temporary);
736
+ if (exclusive) await fs3.link(temporary, file);
737
+ else await fs3.rename(temporary, file);
738
+ } finally {
739
+ await fs3.rm(temporary, { force: true });
740
+ }
741
+ } catch (err) {
742
+ throw explainFsPermission(err, file) ?? err;
551
743
  }
552
744
  }
553
745
 
@@ -944,6 +1136,19 @@ async function releaseCliSeat(opts) {
944
1136
  }
945
1137
 
946
1138
  export {
1139
+ markPrinted,
1140
+ wasPrinted,
1141
+ markExpected,
1142
+ isExpected,
1143
+ networkErrorCode,
1144
+ isNetworkFetchFailure,
1145
+ FS_PERMISSION_CODES,
1146
+ fsPermissionInfo,
1147
+ explainFsPermission,
1148
+ describeError,
1149
+ isBrokenPipe,
1150
+ writeSafely,
1151
+ guardStdio,
947
1152
  excludeFile,
948
1153
  run,
949
1154
  readRemoteSource,
@@ -38,7 +38,7 @@ import fs from "fs";
38
38
  import os from "os";
39
39
  import path from "path";
40
40
  import { fileURLToPath } from "url";
41
- var RAW_CHANNEL = "latest";
41
+ var RAW_CHANNEL = "dev";
42
42
  var CLI_CHANNEL = RAW_CHANNEL === "dev" ? "dev" : "latest";
43
43
  var STANDS = {
44
44
  prod: { api: "https://api.genex.games", dashboard: "https://genex.games" },