@meshy-ai/cli 0.1.3 → 0.3.0

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 (293) hide show
  1. package/.env.example +8 -2
  2. package/README.md +342 -108
  3. package/dist/client/endpoints/animation-catalog.d.ts +37 -0
  4. package/dist/client/endpoints/animation-catalog.d.ts.map +1 -0
  5. package/dist/client/endpoints/animation-catalog.js +55 -0
  6. package/dist/client/endpoints/animation-catalog.js.map +1 -0
  7. package/dist/client/endpoints/balance.d.ts +5 -0
  8. package/dist/client/endpoints/balance.d.ts.map +1 -1
  9. package/dist/client/endpoints/balance.js +5 -1
  10. package/dist/client/endpoints/balance.js.map +1 -1
  11. package/dist/client/endpoints/base.d.ts +41 -7
  12. package/dist/client/endpoints/base.d.ts.map +1 -1
  13. package/dist/client/endpoints/base.js +61 -40
  14. package/dist/client/endpoints/base.js.map +1 -1
  15. package/dist/client/endpoints/showcases.d.ts +31 -0
  16. package/dist/client/endpoints/showcases.d.ts.map +1 -0
  17. package/dist/client/endpoints/showcases.js +49 -0
  18. package/dist/client/endpoints/showcases.js.map +1 -0
  19. package/dist/client/errors.d.ts +1 -0
  20. package/dist/client/errors.d.ts.map +1 -1
  21. package/dist/client/errors.js +1 -1
  22. package/dist/client/errors.js.map +1 -1
  23. package/dist/client/index.d.ts +47 -38
  24. package/dist/client/index.d.ts.map +1 -1
  25. package/dist/client/index.js +89 -120
  26. package/dist/client/index.js.map +1 -1
  27. package/dist/client/resource-registry.d.ts +97 -0
  28. package/dist/client/resource-registry.d.ts.map +1 -0
  29. package/dist/client/resource-registry.js +157 -0
  30. package/dist/client/resource-registry.js.map +1 -0
  31. package/dist/client/transport.d.ts +99 -0
  32. package/dist/client/transport.d.ts.map +1 -0
  33. package/dist/client/transport.js +320 -0
  34. package/dist/client/transport.js.map +1 -0
  35. package/dist/client/types.d.ts +28 -221
  36. package/dist/client/types.d.ts.map +1 -1
  37. package/dist/client/types.js +28 -6
  38. package/dist/client/types.js.map +1 -1
  39. package/dist/cmd/animation-catalog.d.ts +26 -0
  40. package/dist/cmd/animation-catalog.d.ts.map +1 -0
  41. package/dist/cmd/animation-catalog.js +80 -0
  42. package/dist/cmd/animation-catalog.js.map +1 -0
  43. package/dist/cmd/api.d.ts +3 -2
  44. package/dist/cmd/api.d.ts.map +1 -1
  45. package/dist/cmd/api.js +22 -8
  46. package/dist/cmd/api.js.map +1 -1
  47. package/dist/cmd/auth.d.ts.map +1 -1
  48. package/dist/cmd/auth.js +5 -0
  49. package/dist/cmd/auth.js.map +1 -1
  50. package/dist/cmd/balance.d.ts.map +1 -1
  51. package/dist/cmd/balance.js +10 -6
  52. package/dist/cmd/balance.js.map +1 -1
  53. package/dist/cmd/creative-lab.d.ts +26 -0
  54. package/dist/cmd/creative-lab.d.ts.map +1 -0
  55. package/dist/cmd/creative-lab.js +256 -0
  56. package/dist/cmd/creative-lab.js.map +1 -0
  57. package/dist/cmd/delete.d.ts.map +1 -1
  58. package/dist/cmd/delete.js +6 -7
  59. package/dist/cmd/delete.js.map +1 -1
  60. package/dist/cmd/doctor.d.ts +23 -0
  61. package/dist/cmd/doctor.d.ts.map +1 -0
  62. package/dist/cmd/doctor.js +61 -0
  63. package/dist/cmd/doctor.js.map +1 -0
  64. package/dist/cmd/download.d.ts +17 -0
  65. package/dist/cmd/download.d.ts.map +1 -0
  66. package/dist/cmd/download.js +436 -0
  67. package/dist/cmd/download.js.map +1 -0
  68. package/dist/cmd/inspect.d.ts +63 -0
  69. package/dist/cmd/inspect.d.ts.map +1 -0
  70. package/dist/cmd/inspect.js +179 -0
  71. package/dist/cmd/inspect.js.map +1 -0
  72. package/dist/cmd/make.d.ts +13 -3
  73. package/dist/cmd/make.d.ts.map +1 -1
  74. package/dist/cmd/make.js +240 -36
  75. package/dist/cmd/make.js.map +1 -1
  76. package/dist/cmd/mesh.d.ts +13 -0
  77. package/dist/cmd/mesh.d.ts.map +1 -0
  78. package/dist/cmd/mesh.js +67 -0
  79. package/dist/cmd/mesh.js.map +1 -0
  80. package/dist/cmd/project.d.ts +16 -0
  81. package/dist/cmd/project.d.ts.map +1 -0
  82. package/dist/cmd/project.js +136 -0
  83. package/dist/cmd/project.js.map +1 -0
  84. package/dist/cmd/resources.d.ts +5 -0
  85. package/dist/cmd/resources.d.ts.map +1 -1
  86. package/dist/cmd/resources.js +38 -10
  87. package/dist/cmd/resources.js.map +1 -1
  88. package/dist/cmd/showcases.d.ts +10 -0
  89. package/dist/cmd/showcases.d.ts.map +1 -0
  90. package/dist/cmd/showcases.js +63 -0
  91. package/dist/cmd/showcases.js.map +1 -0
  92. package/dist/cmd/slicer.d.ts +12 -0
  93. package/dist/cmd/slicer.d.ts.map +1 -0
  94. package/dist/cmd/slicer.js +46 -0
  95. package/dist/cmd/slicer.js.map +1 -0
  96. package/dist/cmd/uv-unwrap.d.ts +16 -0
  97. package/dist/cmd/uv-unwrap.d.ts.map +1 -0
  98. package/dist/cmd/uv-unwrap.js +52 -0
  99. package/dist/cmd/uv-unwrap.js.map +1 -0
  100. package/dist/index.d.ts +13 -1
  101. package/dist/index.d.ts.map +1 -1
  102. package/dist/index.js +141 -8
  103. package/dist/index.js.map +1 -1
  104. package/dist/internal/artifacts.d.ts +97 -0
  105. package/dist/internal/artifacts.d.ts.map +1 -0
  106. package/dist/internal/artifacts.js +280 -0
  107. package/dist/internal/artifacts.js.map +1 -0
  108. package/dist/internal/atomic-file.d.ts +39 -0
  109. package/dist/internal/atomic-file.d.ts.map +1 -0
  110. package/dist/internal/atomic-file.js +160 -0
  111. package/dist/internal/atomic-file.js.map +1 -0
  112. package/dist/internal/command-helpers.d.ts +46 -0
  113. package/dist/internal/command-helpers.d.ts.map +1 -0
  114. package/dist/internal/command-helpers.js +61 -0
  115. package/dist/internal/command-helpers.js.map +1 -0
  116. package/dist/internal/config.d.ts +48 -5
  117. package/dist/internal/config.d.ts.map +1 -1
  118. package/dist/internal/config.js +95 -6
  119. package/dist/internal/config.js.map +1 -1
  120. package/dist/internal/context.d.ts +25 -0
  121. package/dist/internal/context.d.ts.map +1 -0
  122. package/dist/internal/context.js +37 -0
  123. package/dist/internal/context.js.map +1 -0
  124. package/dist/internal/credentials.d.ts +11 -0
  125. package/dist/internal/credentials.d.ts.map +1 -1
  126. package/dist/internal/credentials.js +2 -0
  127. package/dist/internal/credentials.js.map +1 -1
  128. package/dist/internal/device-state.js.map +1 -1
  129. package/dist/internal/device.d.ts.map +1 -1
  130. package/dist/internal/doctor.d.ts +94 -0
  131. package/dist/internal/doctor.d.ts.map +1 -0
  132. package/dist/internal/doctor.js +280 -0
  133. package/dist/internal/doctor.js.map +1 -0
  134. package/dist/internal/download.d.ts +147 -13
  135. package/dist/internal/download.d.ts.map +1 -1
  136. package/dist/internal/download.js +735 -142
  137. package/dist/internal/download.js.map +1 -1
  138. package/dist/internal/env-file.d.ts +29 -0
  139. package/dist/internal/env-file.d.ts.map +1 -0
  140. package/dist/internal/env-file.js +95 -0
  141. package/dist/internal/env-file.js.map +1 -0
  142. package/dist/internal/errors.d.ts +74 -1
  143. package/dist/internal/errors.d.ts.map +1 -1
  144. package/dist/internal/errors.js +173 -1
  145. package/dist/internal/errors.js.map +1 -1
  146. package/dist/internal/file-input.d.ts +52 -12
  147. package/dist/internal/file-input.d.ts.map +1 -1
  148. package/dist/internal/file-input.js +162 -33
  149. package/dist/internal/file-input.js.map +1 -1
  150. package/dist/internal/flags.js.map +1 -1
  151. package/dist/internal/global-options.d.ts +6 -4
  152. package/dist/internal/global-options.d.ts.map +1 -1
  153. package/dist/internal/global-options.js +19 -4
  154. package/dist/internal/global-options.js.map +1 -1
  155. package/dist/internal/headless.js.map +1 -1
  156. package/dist/internal/image-models.d.ts.map +1 -1
  157. package/dist/internal/inspect.d.ts +67 -0
  158. package/dist/internal/inspect.d.ts.map +1 -0
  159. package/dist/internal/inspect.js +111 -0
  160. package/dist/internal/inspect.js.map +1 -0
  161. package/dist/internal/lock.d.ts +19 -0
  162. package/dist/internal/lock.d.ts.map +1 -0
  163. package/dist/internal/lock.js +66 -0
  164. package/dist/internal/lock.js.map +1 -0
  165. package/dist/internal/logger.d.ts.map +1 -1
  166. package/dist/internal/material-links.d.ts +115 -0
  167. package/dist/internal/material-links.d.ts.map +1 -0
  168. package/dist/internal/material-links.js +577 -0
  169. package/dist/internal/material-links.js.map +1 -0
  170. package/dist/internal/obj-transform.d.ts +115 -0
  171. package/dist/internal/obj-transform.d.ts.map +1 -0
  172. package/dist/internal/obj-transform.js +649 -0
  173. package/dist/internal/obj-transform.js.map +1 -0
  174. package/dist/internal/operation-store.d.ts +123 -0
  175. package/dist/internal/operation-store.d.ts.map +1 -0
  176. package/dist/internal/operation-store.js +229 -0
  177. package/dist/internal/operation-store.js.map +1 -0
  178. package/dist/internal/output.d.ts +20 -2
  179. package/dist/internal/output.d.ts.map +1 -1
  180. package/dist/internal/output.js +32 -4
  181. package/dist/internal/output.js.map +1 -1
  182. package/dist/internal/paths.d.ts +89 -0
  183. package/dist/internal/paths.d.ts.map +1 -0
  184. package/dist/internal/paths.js +168 -0
  185. package/dist/internal/paths.js.map +1 -0
  186. package/dist/internal/payload.d.ts +15 -0
  187. package/dist/internal/payload.d.ts.map +1 -1
  188. package/dist/internal/payload.js +50 -3
  189. package/dist/internal/payload.js.map +1 -1
  190. package/dist/internal/poll.d.ts +30 -4
  191. package/dist/internal/poll.d.ts.map +1 -1
  192. package/dist/internal/poll.js +99 -16
  193. package/dist/internal/poll.js.map +1 -1
  194. package/dist/internal/pricing.d.ts.map +1 -1
  195. package/dist/internal/project-store.d.ts +197 -0
  196. package/dist/internal/project-store.d.ts.map +1 -0
  197. package/dist/internal/project-store.js +467 -0
  198. package/dist/internal/project-store.js.map +1 -0
  199. package/dist/internal/result.d.ts +50 -0
  200. package/dist/internal/result.d.ts.map +1 -0
  201. package/dist/internal/result.js +41 -0
  202. package/dist/internal/result.js.map +1 -0
  203. package/dist/internal/runtime.d.ts +35 -5
  204. package/dist/internal/runtime.d.ts.map +1 -1
  205. package/dist/internal/runtime.js +68 -18
  206. package/dist/internal/runtime.js.map +1 -1
  207. package/dist/internal/slicers.d.ts +117 -0
  208. package/dist/internal/slicers.d.ts.map +1 -0
  209. package/dist/internal/slicers.js +314 -0
  210. package/dist/internal/slicers.js.map +1 -0
  211. package/dist/internal/stream.d.ts +58 -0
  212. package/dist/internal/stream.d.ts.map +1 -0
  213. package/dist/internal/stream.js +280 -0
  214. package/dist/internal/stream.js.map +1 -0
  215. package/dist/internal/task-command.d.ts +128 -11
  216. package/dist/internal/task-command.d.ts.map +1 -1
  217. package/dist/internal/task-command.js +991 -111
  218. package/dist/internal/task-command.js.map +1 -1
  219. package/dist/internal/task-view.d.ts +56 -0
  220. package/dist/internal/task-view.d.ts.map +1 -0
  221. package/dist/internal/task-view.js +95 -0
  222. package/dist/internal/task-view.js.map +1 -0
  223. package/dist/internal/update-notifier.d.ts +5 -1
  224. package/dist/internal/update-notifier.d.ts.map +1 -1
  225. package/dist/internal/update-notifier.js +7 -3
  226. package/dist/internal/update-notifier.js.map +1 -1
  227. package/dist/internal/version.js.map +1 -1
  228. package/dist/root.d.ts +10 -0
  229. package/dist/root.d.ts.map +1 -1
  230. package/dist/root.js +75 -10
  231. package/dist/root.js.map +1 -1
  232. package/package.json +11 -10
  233. package/skills/meshy-cli/SKILL.md +140 -59
  234. package/dist/client/endpoints/analyze-printability.d.ts +0 -5
  235. package/dist/client/endpoints/analyze-printability.d.ts.map +0 -1
  236. package/dist/client/endpoints/analyze-printability.js +0 -7
  237. package/dist/client/endpoints/analyze-printability.js.map +0 -1
  238. package/dist/client/endpoints/animate.d.ts +0 -6
  239. package/dist/client/endpoints/animate.d.ts.map +0 -1
  240. package/dist/client/endpoints/animate.js +0 -8
  241. package/dist/client/endpoints/animate.js.map +0 -1
  242. package/dist/client/endpoints/convert.d.ts +0 -5
  243. package/dist/client/endpoints/convert.d.ts.map +0 -1
  244. package/dist/client/endpoints/convert.js +0 -7
  245. package/dist/client/endpoints/convert.js.map +0 -1
  246. package/dist/client/endpoints/image-to-3d.d.ts +0 -5
  247. package/dist/client/endpoints/image-to-3d.d.ts.map +0 -1
  248. package/dist/client/endpoints/image-to-3d.js +0 -7
  249. package/dist/client/endpoints/image-to-3d.js.map +0 -1
  250. package/dist/client/endpoints/image-to-image.d.ts +0 -5
  251. package/dist/client/endpoints/image-to-image.d.ts.map +0 -1
  252. package/dist/client/endpoints/image-to-image.js +0 -7
  253. package/dist/client/endpoints/image-to-image.js.map +0 -1
  254. package/dist/client/endpoints/multi-color-print.d.ts +0 -5
  255. package/dist/client/endpoints/multi-color-print.d.ts.map +0 -1
  256. package/dist/client/endpoints/multi-color-print.js +0 -7
  257. package/dist/client/endpoints/multi-color-print.js.map +0 -1
  258. package/dist/client/endpoints/multi-image-to-3d.d.ts +0 -5
  259. package/dist/client/endpoints/multi-image-to-3d.d.ts.map +0 -1
  260. package/dist/client/endpoints/multi-image-to-3d.js +0 -7
  261. package/dist/client/endpoints/multi-image-to-3d.js.map +0 -1
  262. package/dist/client/endpoints/remesh.d.ts +0 -5
  263. package/dist/client/endpoints/remesh.d.ts.map +0 -1
  264. package/dist/client/endpoints/remesh.js +0 -7
  265. package/dist/client/endpoints/remesh.js.map +0 -1
  266. package/dist/client/endpoints/repair-printability.d.ts +0 -5
  267. package/dist/client/endpoints/repair-printability.d.ts.map +0 -1
  268. package/dist/client/endpoints/repair-printability.js +0 -7
  269. package/dist/client/endpoints/repair-printability.js.map +0 -1
  270. package/dist/client/endpoints/resize.d.ts +0 -5
  271. package/dist/client/endpoints/resize.d.ts.map +0 -1
  272. package/dist/client/endpoints/resize.js +0 -7
  273. package/dist/client/endpoints/resize.js.map +0 -1
  274. package/dist/client/endpoints/retexture.d.ts +0 -5
  275. package/dist/client/endpoints/retexture.d.ts.map +0 -1
  276. package/dist/client/endpoints/retexture.js +0 -7
  277. package/dist/client/endpoints/retexture.js.map +0 -1
  278. package/dist/client/endpoints/rigging.d.ts +0 -6
  279. package/dist/client/endpoints/rigging.d.ts.map +0 -1
  280. package/dist/client/endpoints/rigging.js +0 -8
  281. package/dist/client/endpoints/rigging.js.map +0 -1
  282. package/dist/client/endpoints/text-to-3d.d.ts +0 -6
  283. package/dist/client/endpoints/text-to-3d.d.ts.map +0 -1
  284. package/dist/client/endpoints/text-to-3d.js +0 -8
  285. package/dist/client/endpoints/text-to-3d.js.map +0 -1
  286. package/dist/client/endpoints/text-to-image.d.ts +0 -5
  287. package/dist/client/endpoints/text-to-image.d.ts.map +0 -1
  288. package/dist/client/endpoints/text-to-image.js +0 -7
  289. package/dist/client/endpoints/text-to-image.js.map +0 -1
  290. package/dist/client/endpoints/text-to-motion.d.ts +0 -6
  291. package/dist/client/endpoints/text-to-motion.d.ts.map +0 -1
  292. package/dist/client/endpoints/text-to-motion.js +0 -8
  293. package/dist/client/endpoints/text-to-motion.js.map +0 -1
@@ -0,0 +1,179 @@
1
+ /**
2
+ * inspect faces — the face-count gate.
3
+ *
4
+ * Two mutually exclusive task sources: a saved task JSON (no network, no
5
+ * credential, no config directory) or exactly one GET of a task through the
6
+ * resource registry. The command never downloads a model and never creates a
7
+ * remesh task; a failing verdict only *describes* the remesh that would help.
8
+ *
9
+ * `--max-faces` is required on purpose: the rigging ceiling (300000) is one
10
+ * consumer's rule and uv-unwrap's is 40000 — there is no limit that belongs
11
+ * to every mesh, so none is assumed. Exit codes: pass 0, fail 12, unknown 13
12
+ * (D-009). Nothing printed here claims more than the face count.
13
+ */
14
+ import { Command } from "commander";
15
+ import { closeSync, fstatSync, openSync, readSync } from "node:fs";
16
+ import { resolve as resolvePath } from "node:path";
17
+ import { findTaskResource, TASK_RESOURCES } from "../client/resource-registry.js";
18
+ import { emitResult, openCommand, rejectOutputFlagForV1, saveRawJson } from "../internal/command-helpers.js";
19
+ import { abortSignal } from "../internal/context.js";
20
+ import { CliError, UsageError } from "../internal/errors.js";
21
+ import { judgeTask, remeshSuggestion } from "../internal/inspect.js";
22
+ import { buildLocalRuntime, buildRuntime } from "../internal/runtime.js";
23
+ import { extractTaskObject } from "../internal/task-view.js";
24
+ /** Engineering limit for a local task JSON (D-018); enforced while reading, never by truncation. */
25
+ export const TASK_JSON_MAX_BYTES = 16 * 1024 * 1024;
26
+ function errnoMessage(err) {
27
+ const code = err?.code;
28
+ return code ? code : err instanceof Error ? err.message : String(err);
29
+ }
30
+ /** `--max-faces` accepts a plain positive integer; "3.5", "0", "-1" and "abc" are usage errors. */
31
+ export function parseMaxFaces(raw) {
32
+ const text = raw.trim();
33
+ if (!/^\d+$/.test(text))
34
+ throw new UsageError(`--max-faces must be a positive integer (got '${raw}')`);
35
+ const n = Number(text);
36
+ if (!Number.isSafeInteger(n) || n < 1)
37
+ throw new UsageError(`--max-faces must be a positive integer (got '${raw}')`);
38
+ return n;
39
+ }
40
+ function readBounded(fd, maxBytes, label) {
41
+ const chunks = [];
42
+ const chunk = Buffer.allocUnsafe(64 * 1024);
43
+ let total = 0;
44
+ for (;;) {
45
+ const n = readSync(fd, chunk, 0, chunk.length, null);
46
+ if (n === 0)
47
+ break;
48
+ total += n;
49
+ if (total > maxBytes) {
50
+ throw new UsageError(`--task-json: ${label} exceeds ${maxBytes} bytes (task JSON limit); refusing to read further`);
51
+ }
52
+ chunks.push(Buffer.from(chunk.subarray(0, n)));
53
+ }
54
+ return Buffer.concat(chunks, total).toString("utf8");
55
+ }
56
+ /**
57
+ * Read a task from a local JSON file in any shape the CLI writes or the API
58
+ * returns (API task, download meta.json, v1 envelope / result). Every problem
59
+ * with the file is a usage error: the caller named it, so a bad file is a
60
+ * mistake to report, not a fact about the model.
61
+ */
62
+ export function readTaskJsonFile(path, opts = {}) {
63
+ const cwd = opts.cwd ?? process.cwd();
64
+ const maxBytes = opts.maxBytes ?? TASK_JSON_MAX_BYTES;
65
+ const abs = resolvePath(cwd, path);
66
+ let fd;
67
+ try {
68
+ fd = openSync(abs, "r");
69
+ }
70
+ catch (err) {
71
+ throw new UsageError(`--task-json: cannot open ${path} (${errnoMessage(err)})`);
72
+ }
73
+ let text;
74
+ try {
75
+ if (!fstatSync(fd).isFile())
76
+ throw new UsageError(`--task-json: not a regular file: ${path}`);
77
+ text = readBounded(fd, maxBytes, path);
78
+ }
79
+ finally {
80
+ closeSync(fd);
81
+ }
82
+ let parsed;
83
+ try {
84
+ parsed = JSON.parse(text);
85
+ }
86
+ catch (err) {
87
+ throw new UsageError(`--task-json: ${path} is not valid JSON (${err instanceof Error ? err.message : String(err)})`);
88
+ }
89
+ const extracted = extractTaskObject(parsed);
90
+ if (!extracted) {
91
+ throw new UsageError(`--task-json: ${path} does not contain a task (expected an API task object with id+status, a download meta.json with "task", or a v1 envelope with result.task)`);
92
+ }
93
+ return { path: abs, task: extracted.task, shape: extracted.source };
94
+ }
95
+ function resolveSource(opts) {
96
+ const hasFile = opts.taskJson !== undefined;
97
+ const hasApi = opts.resource !== undefined || opts.taskId !== undefined;
98
+ if (hasFile && hasApi) {
99
+ throw new UsageError("choose one task source: --task-json <file>, or --resource <id> with --task-id <id> — not both");
100
+ }
101
+ if (hasFile)
102
+ return { kind: "task-json", path: opts.taskJson };
103
+ if (opts.resource === undefined || opts.taskId === undefined) {
104
+ throw new UsageError("a task source is required: --task-json <file>, or --resource <id> together with --task-id <id>");
105
+ }
106
+ return { kind: "api", resource: opts.resource, taskId: opts.taskId };
107
+ }
108
+ function taskIdOf(task, fallback) {
109
+ if (typeof task["id"] === "string" && task["id"])
110
+ return task["id"];
111
+ if (typeof task["task_id"] === "string" && task["task_id"])
112
+ return task["task_id"];
113
+ return fallback;
114
+ }
115
+ /** Build a fresh `inspect` command tree (tests parse a new tree per run). */
116
+ export function buildInspectCommand() {
117
+ const faces = new Command("faces")
118
+ .description("Face-count gate: pass (exit 0) | fail (exit 12) | unknown (exit 13). Answers only whether face_count <= --max-faces; " +
119
+ "a missing or malformed count is unknown, never 0. Never downloads a model or submits a remesh")
120
+ .option("--task-json <file>", "saved task JSON (API task, download meta.json or v1 envelope); no network, no credential")
121
+ .option("--resource <id>", "task resource id (see `meshy resources`), fetched once with --task-id")
122
+ .option("--task-id <id>", "task id to fetch once from the API (with --resource)")
123
+ .option("--max-faces <n>", "maximum accepted face count (required; e.g. 300000 for rigging, 40000 for uv-unwrap — no default)", parseMaxFaces)
124
+ .option("--save-json <file>", "API source only: save the task JSON as received (never overwrites)")
125
+ .action(async (opts, thisCmd) => {
126
+ const opened = openCommand(thisCmd, "inspect.faces", "v1");
127
+ rejectOutputFlagForV1(opened, opts.saveJson);
128
+ if (opts.maxFaces === undefined) {
129
+ throw new UsageError("--max-faces <n> is required: the limit belongs to the caller (rigging accepts up to 300000 faces, uv-unwrap up to 40000); no default is assumed");
130
+ }
131
+ const limit = opts.maxFaces;
132
+ const source = resolveSource(opts);
133
+ let task;
134
+ let sourceInfo;
135
+ let savedJson = null;
136
+ if (source.kind === "task-json") {
137
+ if (opts.saveJson) {
138
+ throw new UsageError("--save-json only applies to the API source (--resource/--task-id); the task JSON is already a file");
139
+ }
140
+ buildLocalRuntime(opened.flags);
141
+ const loaded = readTaskJsonFile(source.path);
142
+ task = loaded.task;
143
+ sourceInfo = { kind: "task-json", path: loaded.path, shape: loaded.shape };
144
+ }
145
+ else {
146
+ const descriptor = findTaskResource(source.resource);
147
+ if (!descriptor) {
148
+ throw new UsageError(`unknown --resource '${source.resource}'. Valid ids: ${TASK_RESOURCES.map((d) => d.id).join(", ")}`);
149
+ }
150
+ const runtime = await buildRuntime(opened.flags);
151
+ // Exactly one GET; the verdict is read from the raw JSON, not the Zod-defaulted object.
152
+ const { raw } = await runtime.client.endpointFor(descriptor).retrieveDetailed(source.taskId, { signal: abortSignal() });
153
+ const extracted = extractTaskObject(raw);
154
+ task = extracted?.task ?? raw;
155
+ savedJson = opts.saveJson ? saveRawJson(opts.saveJson, raw, { workspace: opened.flags.workspaceRoot }) : null;
156
+ sourceInfo = { kind: "api", resource: descriptor.id, task_id: source.taskId, endpoint: descriptor.legacyEndpoint, requests_made: 1 };
157
+ }
158
+ const verdict = judgeTask(task, limit);
159
+ const taskId = taskIdOf(task, source.kind === "api" ? source.taskId : null);
160
+ const result = {
161
+ ...verdict,
162
+ source: sourceInfo,
163
+ task_id: taskId,
164
+ status: typeof task["status"] === "string" ? task["status"] : null,
165
+ suggestion: verdict.verdict === "fail" ? remeshSuggestion(taskId, limit) : null,
166
+ saved_json: savedJson,
167
+ };
168
+ if (verdict.verdict === "fail") {
169
+ throw new CliError({ code: "check_failed", message: `face count check failed: ${verdict.reason}`, result: { ...result } });
170
+ }
171
+ if (verdict.verdict === "unknown") {
172
+ throw new CliError({ code: "check_unknown", message: `face count unknown: ${verdict.reason}`, result: { ...result } });
173
+ }
174
+ await emitResult(opened, result, result);
175
+ });
176
+ return new Command("inspect").description("Local checks on task results (face-count gate)").addCommand(faces);
177
+ }
178
+ export const inspectCommand = buildInspectCommand();
179
+ //# sourceMappingURL=inspect.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"inspect.js","sourceRoot":"","sources":["../../src/cmd/inspect.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AACnE,OAAO,EAAE,OAAO,IAAI,WAAW,EAAE,MAAM,WAAW,CAAC;AACnD,OAAO,EAAE,gBAAgB,EAAE,cAAc,EAAE,MAAM,gCAAgC,CAAC;AAClF,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,qBAAqB,EAAE,WAAW,EAAkB,MAAM,gCAAgC,CAAC;AAC7H,OAAO,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AACrD,OAAO,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AAC7D,OAAO,EAAE,SAAS,EAAE,gBAAgB,EAAyB,MAAM,wBAAwB,CAAC;AAC5F,OAAO,EAAE,iBAAiB,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AACzE,OAAO,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAE7D,oGAAoG;AACpG,MAAM,CAAC,MAAM,mBAAmB,GAAG,EAAE,GAAG,IAAI,GAAG,IAAI,CAAC;AAoBpD,SAAS,YAAY,CAAC,GAAY;IAChC,MAAM,IAAI,GAAI,GAAyC,EAAE,IAAI,CAAC;IAC9D,OAAO,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;AACxE,CAAC;AAED,mGAAmG;AACnG,MAAM,UAAU,aAAa,CAAC,GAAW;IACvC,MAAM,IAAI,GAAG,GAAG,CAAC,IAAI,EAAE,CAAC;IACxB,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC;QAAE,MAAM,IAAI,UAAU,CAAC,gDAAgD,GAAG,IAAI,CAAC,CAAC;IACvG,MAAM,CAAC,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC;IACvB,IAAI,CAAC,MAAM,CAAC,aAAa,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC;QAAE,MAAM,IAAI,UAAU,CAAC,gDAAgD,GAAG,IAAI,CAAC,CAAC;IACrH,OAAO,CAAC,CAAC;AACX,CAAC;AAED,SAAS,WAAW,CAAC,EAAU,EAAE,QAAgB,EAAE,KAAa;IAC9D,MAAM,MAAM,GAAa,EAAE,CAAC;IAC5B,MAAM,KAAK,GAAG,MAAM,CAAC,WAAW,CAAC,EAAE,GAAG,IAAI,CAAC,CAAC;IAC5C,IAAI,KAAK,GAAG,CAAC,CAAC;IACd,SAAS,CAAC;QACR,MAAM,CAAC,GAAG,QAAQ,CAAC,EAAE,EAAE,KAAK,EAAE,CAAC,EAAE,KAAK,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;QACrD,IAAI,CAAC,KAAK,CAAC;YAAE,MAAM;QACnB,KAAK,IAAI,CAAC,CAAC;QACX,IAAI,KAAK,GAAG,QAAQ,EAAE,CAAC;YACrB,MAAM,IAAI,UAAU,CAAC,gBAAgB,KAAK,YAAY,QAAQ,oDAAoD,CAAC,CAAC;QACtH,CAAC;QACD,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;IACjD,CAAC;IACD,OAAO,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;AACvD,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,gBAAgB,CAAC,IAAY,EAAE,IAAI,GAAwC,EAAE;IAC3F,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,IAAI,OAAO,CAAC,GAAG,EAAE,CAAC;IACtC,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,IAAI,mBAAmB,CAAC;IACtD,MAAM,GAAG,GAAG,WAAW,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;IACnC,IAAI,EAAU,CAAC;IACf,IAAI,CAAC;QACH,EAAE,GAAG,QAAQ,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;IAC1B,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,IAAI,UAAU,CAAC,4BAA4B,IAAI,KAAK,YAAY,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IAClF,CAAC;IACD,IAAI,IAAY,CAAC;IACjB,IAAI,CAAC;QACH,IAAI,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC,MAAM,EAAE;YAAE,MAAM,IAAI,UAAU,CAAC,oCAAoC,IAAI,EAAE,CAAC,CAAC;QAC9F,IAAI,GAAG,WAAW,CAAC,EAAE,EAAE,QAAQ,EAAE,IAAI,CAAC,CAAC;IACzC,CAAC;YAAS,CAAC;QACT,SAAS,CAAC,EAAE,CAAC,CAAC;IAChB,CAAC;IACD,IAAI,MAAe,CAAC;IACpB,IAAI,CAAC;QACH,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAC5B,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,IAAI,UAAU,CAAC,gBAAgB,IAAI,uBAAuB,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IACvH,CAAC;IACD,MAAM,SAAS,GAAG,iBAAiB,CAAC,MAAM,CAAC,CAAC;IAC5C,IAAI,CAAC,SAAS,EAAE,CAAC;QACf,MAAM,IAAI,UAAU,CAClB,gBAAgB,IAAI,4IAA4I,CACjK,CAAC;IACJ,CAAC;IACD,OAAO,EAAE,IAAI,EAAE,GAAG,EAAE,IAAI,EAAE,SAAS,CAAC,IAAI,EAAE,KAAK,EAAE,SAAS,CAAC,MAAM,EAAE,CAAC;AACtE,CAAC;AAED,SAAS,aAAa,CAAC,IAAkB;IACvC,MAAM,OAAO,GAAG,IAAI,CAAC,QAAQ,KAAK,SAAS,CAAC;IAC5C,MAAM,MAAM,GAAG,IAAI,CAAC,QAAQ,KAAK,SAAS,IAAI,IAAI,CAAC,MAAM,KAAK,SAAS,CAAC;IACxE,IAAI,OAAO,IAAI,MAAM,EAAE,CAAC;QACtB,MAAM,IAAI,UAAU,CAAC,+FAA+F,CAAC,CAAC;IACxH,CAAC;IACD,IAAI,OAAO;QAAE,OAAO,EAAE,IAAI,EAAE,WAAW,EAAE,IAAI,EAAE,IAAI,CAAC,QAAS,EAAE,CAAC;IAChE,IAAI,IAAI,CAAC,QAAQ,KAAK,SAAS,IAAI,IAAI,CAAC,MAAM,KAAK,SAAS,EAAE,CAAC;QAC7D,MAAM,IAAI,UAAU,CAAC,gGAAgG,CAAC,CAAC;IACzH,CAAC;IACD,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,QAAQ,EAAE,IAAI,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,CAAC;AACvE,CAAC;AAED,SAAS,QAAQ,CAAC,IAA6B,EAAE,QAAuB;IACtE,IAAI,OAAO,IAAI,CAAC,IAAI,CAAC,KAAK,QAAQ,IAAI,IAAI,CAAC,IAAI,CAAC;QAAE,OAAO,IAAI,CAAC,IAAI,CAAW,CAAC;IAC9E,IAAI,OAAO,IAAI,CAAC,SAAS,CAAC,KAAK,QAAQ,IAAI,IAAI,CAAC,SAAS,CAAC;QAAE,OAAO,IAAI,CAAC,SAAS,CAAW,CAAC;IAC7F,OAAO,QAAQ,CAAC;AAClB,CAAC;AAkBD,6EAA6E;AAC7E,MAAM,UAAU,mBAAmB;IACjC,MAAM,KAAK,GAAG,IAAI,OAAO,CAAC,OAAO,CAAC;SAC/B,WAAW,CACV,uHAAuH;QACrH,+FAA+F,CAClG;SACA,MAAM,CAAC,oBAAoB,EAAE,0FAA0F,CAAC;SACxH,MAAM,CAAC,iBAAiB,EAAE,uEAAuE,CAAC;SAClG,MAAM,CAAC,gBAAgB,EAAE,sDAAsD,CAAC;SAChF,MAAM,CAAC,iBAAiB,EAAE,mGAAmG,EAAE,aAAa,CAAC;SAC7I,MAAM,CAAC,oBAAoB,EAAE,oEAAoE,CAAC;SAClG,MAAM,CAAC,KAAK,EAAE,IAAkB,EAAE,OAAgB,EAAE,EAAE;QACrD,MAAM,MAAM,GAAG,WAAW,CAAC,OAAO,EAAE,eAAe,EAAE,IAAI,CAAC,CAAC;QAC3D,qBAAqB,CAAC,MAAM,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC;QAC7C,IAAI,IAAI,CAAC,QAAQ,KAAK,SAAS,EAAE,CAAC;YAChC,MAAM,IAAI,UAAU,CAClB,iJAAiJ,CAClJ,CAAC;QACJ,CAAC;QACD,MAAM,KAAK,GAAG,IAAI,CAAC,QAAQ,CAAC;QAC5B,MAAM,MAAM,GAAG,aAAa,CAAC,IAAI,CAAC,CAAC;QAEnC,IAAI,IAA6B,CAAC;QAClC,IAAI,UAAiC,CAAC;QACtC,IAAI,SAAS,GAAqB,IAAI,CAAC;QACvC,IAAI,MAAM,CAAC,IAAI,KAAK,WAAW,EAAE,CAAC;YAChC,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;gBAClB,MAAM,IAAI,UAAU,CAAC,oGAAoG,CAAC,CAAC;YAC7H,CAAC;YACD,iBAAiB,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;YAChC,MAAM,MAAM,GAAG,gBAAgB,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;YAC7C,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC;YACnB,UAAU,GAAG,EAAE,IAAI,EAAE,WAAW,EAAE,IAAI,EAAE,MAAM,CAAC,IAAI,EAAE,KAAK,EAAE,MAAM,CAAC,KAAK,EAAE,CAAC;QAC7E,CAAC;aAAM,CAAC;YACN,MAAM,UAAU,GAAG,gBAAgB,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;YACrD,IAAI,CAAC,UAAU,EAAE,CAAC;gBAChB,MAAM,IAAI,UAAU,CAAC,uBAAuB,MAAM,CAAC,QAAQ,iBAAiB,cAAc,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;YAC5H,CAAC;YACD,MAAM,OAAO,GAAG,MAAM,YAAY,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;YACjD,wFAAwF;YACxF,MAAM,EAAE,GAAG,EAAE,GAAG,MAAM,OAAO,CAAC,MAAM,CAAC,WAAW,CAAC,UAAU,CAAC,CAAC,gBAAgB,CAAC,MAAM,CAAC,MAAM,EAAE,EAAE,MAAM,EAAE,WAAW,EAAE,EAAE,CAAC,CAAC;YACxH,MAAM,SAAS,GAAG,iBAAiB,CAAC,GAAG,CAAC,CAAC;YACzC,IAAI,GAAG,SAAS,EAAE,IAAI,IAAK,GAA+B,CAAC;YAC3D,SAAS,GAAG,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,WAAW,CAAC,IAAI,CAAC,QAAQ,EAAE,GAAG,EAAE,EAAE,SAAS,EAAE,MAAM,CAAC,KAAK,CAAC,aAAa,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;YAC9G,UAAU,GAAG,EAAE,IAAI,EAAE,KAAK,EAAE,QAAQ,EAAE,UAAU,CAAC,EAAE,EAAE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,QAAQ,EAAE,UAAU,CAAC,cAAc,EAAE,aAAa,EAAE,CAAC,EAAE,CAAC;QACvI,CAAC;QAED,MAAM,OAAO,GAAG,SAAS,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;QACvC,MAAM,MAAM,GAAG,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,IAAI,KAAK,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;QAC5E,MAAM,MAAM,GAAgB;YAC1B,GAAG,OAAO;YACV,MAAM,EAAE,UAAU;YAClB,OAAO,EAAE,MAAM;YACf,MAAM,EAAE,OAAO,IAAI,CAAC,QAAQ,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAE,IAAI,CAAC,QAAQ,CAAY,CAAC,CAAC,CAAC,IAAI;YAC9E,UAAU,EAAE,OAAO,CAAC,OAAO,KAAK,MAAM,CAAC,CAAC,CAAC,gBAAgB,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI;YAC/E,UAAU,EAAE,SAAS;SACtB,CAAC;QACF,IAAI,OAAO,CAAC,OAAO,KAAK,MAAM,EAAE,CAAC;YAC/B,MAAM,IAAI,QAAQ,CAAC,EAAE,IAAI,EAAE,cAAc,EAAE,OAAO,EAAE,4BAA4B,OAAO,CAAC,MAAM,EAAE,EAAE,MAAM,EAAE,EAAE,GAAG,MAAM,EAAE,EAAE,CAAC,CAAC;QAC7H,CAAC;QACD,IAAI,OAAO,CAAC,OAAO,KAAK,SAAS,EAAE,CAAC;YAClC,MAAM,IAAI,QAAQ,CAAC,EAAE,IAAI,EAAE,eAAe,EAAE,OAAO,EAAE,uBAAuB,OAAO,CAAC,MAAM,EAAE,EAAE,MAAM,EAAE,EAAE,GAAG,MAAM,EAAE,EAAE,CAAC,CAAC;QACzH,CAAC;QACD,MAAM,UAAU,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;IAC3C,CAAC,CAAC,CAAC;IAEL,OAAO,IAAI,OAAO,CAAC,SAAS,CAAC,CAAC,WAAW,CAAC,gDAAgD,CAAC,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC;AAChH,CAAC;AAED,MAAM,CAAC,MAAM,cAAc,GAAG,mBAAmB,EAAE,CAAC"}
@@ -14,12 +14,21 @@
14
14
  * someone else's credits on its own opinion. The resource commands remain the
15
15
  * way to compose anything else.
16
16
  *
17
- * When a later step fails, the error carries the finished step's task id and
18
- * the command that resumes from it, so a retry never re-runs — or re-bills —
19
- * work that already succeeded.
17
+ * Submission goes through the same journal → single POST → journal primitive
18
+ * as the resource commands (`submitCreate`), so there is one state machine for
19
+ * accepted / rejected / unknown and a local journal failure after acceptance is
20
+ * local_io with the known task id — never "unknown".
21
+ *
22
+ * Three ways to stop early, none of them a re-run:
23
+ * --async submit step 1 and return immediately (no polling);
24
+ * `pending_steps` lists what was not executed.
25
+ * --stop-after-first run step 1 to completion, then return the resume plan.
26
+ * a failed later step keeps the finished step's task id and the command that
27
+ * resumes from it, so a retry never re-bills done work.
20
28
  */
21
29
  import { Command } from "commander";
22
30
  import { type MakePlan } from "../internal/make-plan.js";
31
+ import { warning } from "../internal/result.js";
23
32
  import { type Runtime } from "../internal/runtime.js";
24
33
  export declare const makeCommand: Command;
25
34
  /**
@@ -28,4 +37,5 @@ export declare const makeCommand: Command;
28
37
  * consumes); the image route is a single task with nothing to resume.
29
38
  */
30
39
  export declare function resumeCommand(plan: MakePlan, completedTaskId: string, runtime: Runtime): string | undefined;
40
+ export { warning as _makeWarning };
31
41
  //# sourceMappingURL=make.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"make.d.ts","sourceRoot":"","sources":["../../src/cmd/make.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,OAAO,EAAE,OAAO,EAAU,MAAM,WAAW,CAAC;AAK5C,OAAO,EAAY,KAAK,QAAQ,EAAiB,MAAM,0BAA0B,CAAC;AAIlF,OAAO,EAAiC,KAAK,OAAO,EAAE,MAAM,wBAAwB,CAAC;AAiBrF,eAAO,MAAM,WAAW,SAuCpB,CAAC;AAqGL;;;;GAIG;AACH,wBAAgB,aAAa,CAC3B,IAAI,EAAE,QAAQ,EACd,eAAe,EAAE,MAAM,EACvB,OAAO,EAAE,OAAO,GACf,MAAM,GAAG,SAAS,CAIpB"}
1
+ {"version":3,"file":"make.d.ts","sourceRoot":"","sources":["../../src/cmd/make.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AAEH,OAAO,EAAE,OAAO,EAAU,MAAM,WAAW,CAAC;AAM5C,OAAO,EAAY,KAAK,QAAQ,EAAiB,MAAM,0BAA0B,CAAC;AAMlF,OAAO,EAAc,OAAO,EAAgB,MAAM,uBAAuB,CAAC;AAC1E,OAAO,EAAgB,KAAK,OAAO,EAAE,MAAM,wBAAwB,CAAC;AA0BpE,eAAO,MAAM,WAAW,SAiDpB,CAAC;AAuSL;;;;GAIG;AACH,wBAAgB,aAAa,CAC3B,IAAI,EAAE,QAAQ,EACd,eAAe,EAAE,MAAM,EACvB,OAAO,EAAE,OAAO,GACf,MAAM,GAAG,SAAS,CAIpB;AAmFD,OAAO,EAAE,OAAO,IAAI,YAAY,EAAE,CAAC"}
package/dist/cmd/make.js CHANGED
@@ -14,21 +14,34 @@
14
14
  * someone else's credits on its own opinion. The resource commands remain the
15
15
  * way to compose anything else.
16
16
  *
17
- * When a later step fails, the error carries the finished step's task id and
18
- * the command that resumes from it, so a retry never re-runs — or re-bills —
19
- * work that already succeeded.
17
+ * Submission goes through the same journal → single POST → journal primitive
18
+ * as the resource commands (`submitCreate`), so there is one state machine for
19
+ * accepted / rejected / unknown and a local journal failure after acceptance is
20
+ * local_io with the known task id — never "unknown".
21
+ *
22
+ * Three ways to stop early, none of them a re-run:
23
+ * --async submit step 1 and return immediately (no polling);
24
+ * `pending_steps` lists what was not executed.
25
+ * --stop-after-first run step 1 to completion, then return the resume plan.
26
+ * a failed later step keeps the finished step's task id and the command that
27
+ * resumes from it, so a retry never re-bills done work.
20
28
  */
21
29
  import { Command, Option } from "commander";
22
- import { HintedError, UsageError } from "../internal/errors.js";
30
+ import { abortSignal, wasInterrupted } from "../internal/context.js";
31
+ import { CliError, HintedError, UsageError } from "../internal/errors.js";
23
32
  import { parseInt10 } from "../internal/flags.js";
24
- import { resolveImageFields } from "../internal/file-input.js";
33
+ import { normalizeMediaPayload } from "../internal/file-input.js";
25
34
  import { logger } from "../internal/logger.js";
26
35
  import { planMake } from "../internal/make-plan.js";
27
- import { emit } from "../internal/output.js";
28
- import { pollUntilTerminal } from "../internal/poll.js";
36
+ import { emit, emitEnvelope } from "../internal/output.js";
37
+ import { emitResult, openCommand } from "../internal/command-helpers.js";
38
+ import { downloadArtifacts } from "../internal/download.js";
39
+ import { parseTimeoutSeconds, pollUntilTerminal } from "../internal/poll.js";
29
40
  import { PRICING_DOCS } from "../internal/pricing.js";
30
- import { buildRuntime, readGlobalFlags } from "../internal/runtime.js";
31
- import { emitTerminalOutcome } from "../internal/task-command.js";
41
+ import { okEnvelope, warning } from "../internal/result.js";
42
+ import { buildRuntime } from "../internal/runtime.js";
43
+ import { buildTaskResult, emitTerminalOutcome, preflightOutputPath, submitCreate, taskNextCommands, wrapWithResult, } from "../internal/task-command.js";
44
+ import { requireTaskResource } from "../client/resource-registry.js";
32
45
  const DESCRIPTION = "Generate a 3D model in one command. A prompt runs the two-stage text flow " +
33
46
  "(preview then refine); an image runs the single textured image-to-3d task. " +
34
47
  "Use -o to download the result. Compose anything else from the resource commands";
@@ -38,11 +51,16 @@ export const makeCommand = new Command("make")
38
51
  .addOption(new Option("--dry-run", "print the planned steps and the estimate, then exit without calling the API").default(false))
39
52
  .addOption(new Option("--max-credits <n>", "refuse to start when the estimate exceeds this budget")
40
53
  .argParser(parseInt10))
41
- .addOption(new Option("--async", "start the first step and return its task id instead of running the whole chain").default(false))
54
+ .addOption(new Option("--async", "submit the first step and return its task id immediately (no polling); later steps are reported as pending").default(false))
55
+ .addOption(new Option("--stop-after-first", "run the first step to completion, then return the resume plan instead of continuing").default(false))
42
56
  .addOption(new Option("--timeout <seconds>", "max seconds to poll each step").default("600"))
43
57
  .action(async (input, opts, thisCmd) => {
58
+ const opened = openCommand(thisCmd, "make", "legacy");
59
+ if (opts.async && opts.stopAfterFirst) {
60
+ throw new UsageError("--async and --stop-after-first are mutually exclusive (--async submits and returns; --stop-after-first waits for step 1)");
61
+ }
62
+ const timeoutSeconds = parseTimeoutSeconds(opts.timeout ?? "600");
44
63
  const plan = planMake(input);
45
- const flags = readGlobalFlags(thisCmd);
46
64
  // Budget check before anything is created: refusing costs nothing, and a
47
65
  // refusal after step one has already billed is not a budget at all.
48
66
  if (opts.maxCredits !== undefined && plan.estimatedCredits > opts.maxCredits) {
@@ -50,41 +68,150 @@ export const makeCommand = new Command("make")
50
68
  `(${describeChain(plan)}). Nothing was started.`);
51
69
  }
52
70
  if (opts.dryRun) {
53
- emit(planPayload(plan), { format: flags.format });
71
+ await emitResult(opened, planPayload(plan), { ...planPayload(plan), dry_run: true, requests_made: 0 });
54
72
  return;
55
73
  }
56
- await runChain(plan, opts, await buildRuntime(flags));
74
+ await runChain(plan, opts, timeoutSeconds, await buildRuntime(opened.flags), opened);
57
75
  });
58
- async function runChain(plan, opts, runtime) {
59
- const timeoutSeconds = Number(opts.timeout ?? 600);
76
+ function pendingSteps(plan, from, parentTaskId, output) {
77
+ return plan.steps.slice(from).map((s) => ({
78
+ step: s.index,
79
+ resource: s.resource,
80
+ action: s.action,
81
+ estimated_credits: s.credits,
82
+ requires: `step ${s.index - 1} SUCCEEDED`,
83
+ command: s.resource === "text-to-3d" && s.action === "refine"
84
+ ? `meshy text-to-3d create --mode refine --preview-task-id ${parentTaskId ?? "<preview-task-id>"}${output ? ` -o ${output}` : ""}`
85
+ : null,
86
+ }));
87
+ }
88
+ async function runChain(plan, opts, timeoutSeconds, runtime, opened) {
60
89
  const payloadFor = await buildPayloads(plan);
90
+ // -o is checked before the first billable request: an unwritable or
91
+ // out-of-workspace target must refuse while the run is still free.
92
+ if (runtime.flags.output)
93
+ preflightOutputPath(runtime.flags.output, runtime.flags.workspaceRoot);
94
+ const executed = [];
95
+ const warnings = [];
61
96
  /** Task id of the last step that reached SUCCEEDED — what a resume hangs off. */
62
97
  let completedTaskId = "";
63
98
  for (const step of plan.steps) {
64
99
  const endpoint = endpointFor(step, runtime);
100
+ const descriptor = requireTaskResource(step.resource);
65
101
  const payload = payloadFor[step.index - 1]?.(completedTaskId) ?? {};
66
102
  logger.debug(`make step ${step.index} payload`, payload);
67
103
  // The id is announced before polling starts: an interrupted run leaves a
68
104
  // task progressing server-side, and the caller needs its id to exist
69
105
  // somewhere other than this process's memory.
70
- const taskId = await endpoint.create(payload);
71
- announceStart(plan, step, taskId);
72
- const started = Date.now();
73
- const { task, timedOut } = await pollUntilTerminal(endpoint, taskId, {
74
- timeoutSeconds,
75
- intervalMs: runtime.config.pollIntervalMs,
106
+ const submitted = await submitCreate(runtime, descriptor, endpoint, payload, null, {
107
+ label: `make: the ${step.label} request`,
108
+ extraResult: { step: step.index, route: plan.route, executed: [...executed] },
76
109
  });
77
- const elapsed = (Date.now() - started) / 1000;
110
+ const { taskId, operationId } = submitted;
111
+ warnings.push(...submitted.warnings);
112
+ announceStart(plan, step, taskId);
113
+ executed.push({ step: step.index, resource: step.resource, action: step.action, task_id: taskId, status: null, operation_id: operationId });
114
+ const submission = { state: "accepted", operation_id: operationId, task_id: taskId };
115
+ if (opts.async) {
116
+ // Exactly one POST, zero polls: the caller owns the rest of the plan.
117
+ const pending = pendingSteps(plan, step.index, null, runtime.flags.output);
118
+ if (opened.schema === "v1") {
119
+ await emitEnvelope(okEnvelope("make", {
120
+ route: plan.route,
121
+ submitted: { step: step.index, resource: step.resource, action: step.action, task_id: taskId, operation_id: operationId },
122
+ submission,
123
+ task: null,
124
+ executed,
125
+ pending_steps: pending,
126
+ estimated_credits: plan.estimatedCredits,
127
+ next: taskNextCommands(descriptor, taskId),
128
+ }, warnings), opened.format);
129
+ return;
130
+ }
131
+ emit({
132
+ command: "make",
133
+ route: plan.route,
134
+ submitted: step.action,
135
+ task_id: taskId,
136
+ status: null,
137
+ operation_id: operationId,
138
+ pending_steps: pending,
139
+ hint: `meshy ${step.resource} wait ${taskId}`,
140
+ }, { format: runtime.flags.format });
141
+ return;
142
+ }
143
+ const started = performance.now();
144
+ let poll;
145
+ try {
146
+ poll = await pollUntilTerminal(endpoint, taskId, {
147
+ timeoutSeconds,
148
+ intervalMs: runtime.config.pollIntervalMs,
149
+ requestTimeoutMs: runtime.config.readTimeoutMs,
150
+ signal: abortSignal(),
151
+ });
152
+ }
153
+ catch (err) {
154
+ const context = {
155
+ route: plan.route,
156
+ executed,
157
+ task_id: taskId,
158
+ submission,
159
+ task: null,
160
+ pending_steps: pendingSteps(plan, step.index, null, runtime.flags.output),
161
+ next: taskNextCommands(descriptor, taskId),
162
+ };
163
+ if (wasInterrupted() || abortSignal().aborted) {
164
+ throw new CliError({
165
+ code: "interrupted",
166
+ message: `make: interrupted while waiting for ${step.label} (task ${taskId}); the server keeps running it`,
167
+ recovery: { action: "wait", automatic: false, command: `meshy ${step.resource} wait ${taskId}` },
168
+ result: context,
169
+ });
170
+ }
171
+ // A polling failure is not "no task": the id and the resume command travel with the error.
172
+ throw wrapWithResult(err, context);
173
+ }
174
+ const { task, raw, timedOut, aborted } = poll;
175
+ const elapsed = (performance.now() - started) / 1000;
176
+ executed[executed.length - 1].status = task?.status ?? null;
78
177
  announceOutcome(task, timedOut, elapsed);
79
- if (timedOut || task.status !== "SUCCEEDED") {
80
- throw stepFailure(plan, step, task, timedOut, completedTaskId, runtime);
178
+ if (aborted) {
179
+ throw new CliError({
180
+ code: "interrupted",
181
+ message: `make: interrupted while waiting for ${step.label} (task ${taskId}); the server keeps running it`,
182
+ recovery: { action: "wait", automatic: false, command: `meshy ${step.resource} wait ${taskId}` },
183
+ result: {
184
+ route: plan.route,
185
+ executed,
186
+ task_id: taskId,
187
+ submission,
188
+ task: task ? buildTaskResult({ task, raw, descriptor, includeRaw: false, submission }).task : null,
189
+ pending_steps: pendingSteps(plan, step.index, null, runtime.flags.output),
190
+ next: taskNextCommands(descriptor, taskId),
191
+ },
192
+ });
193
+ }
194
+ if (timedOut || !task || task.status !== "SUCCEEDED") {
195
+ throw stepFailure(plan, step, task, taskId, raw, timedOut, completedTaskId, runtime, executed, opened);
81
196
  }
82
197
  if (step.index === plan.steps.length) {
83
- await emitTerminalOutcome(task, false, elapsed, step.resource, runtime);
198
+ await finalOutcome(opened, runtime, plan, step, task, raw, elapsed, executed, warnings);
84
199
  return;
85
200
  }
86
201
  completedTaskId = task.id;
87
- if (opts.async) {
202
+ if (opts.stopAfterFirst) {
203
+ const pending = pendingSteps(plan, step.index, task.id, runtime.flags.output);
204
+ if (opened.schema === "v1") {
205
+ await emitEnvelope(okEnvelope("make", {
206
+ route: plan.route,
207
+ stopped_after: { step: step.index, resource: step.resource, action: step.action, task_id: task.id, status: task.status },
208
+ task: buildTaskResult({ task, raw, descriptor, includeRaw: false, submission }).task,
209
+ executed,
210
+ pending_steps: pending,
211
+ resume: resumeCommand(plan, task.id, runtime) ?? null,
212
+ }, warnings), opened.format);
213
+ return;
214
+ }
88
215
  emit({
89
216
  command: "make",
90
217
  route: plan.route,
@@ -97,6 +224,64 @@ async function runChain(plan, opts, runtime) {
97
224
  }
98
225
  }
99
226
  }
227
+ async function finalOutcome(opened, runtime, plan, step, task, raw, elapsed, executed, warnings) {
228
+ const descriptor = requireTaskResource(step.resource);
229
+ const submission = { state: "accepted", operation_id: executed[executed.length - 1]?.operation_id ?? null, task_id: task.id };
230
+ if (opened.schema !== "v1") {
231
+ // Legacy shape, same rule as the resource commands: a download failure
232
+ // after the chain succeeded still names the task, its submission and the
233
+ // command that fetches the assets again.
234
+ try {
235
+ await emitTerminalOutcome(task, false, elapsed, step.resource, runtime);
236
+ }
237
+ catch (err) {
238
+ const wrapped = wrapWithResult(err, { route: plan.route, executed, task_id: task.id, submission, next: taskNextCommands(descriptor, task.id) });
239
+ throw new CliError({
240
+ code: wrapped.code,
241
+ message: wrapped.message,
242
+ exitCode: wrapped.exitCode,
243
+ httpStatus: wrapped.httpStatus,
244
+ retryable: wrapped.retryable,
245
+ recovery: wrapped.recovery ?? { action: "download", automatic: false, command: `meshy download --resource ${step.resource} --task-id ${task.id} --all --output-dir <dir>` },
246
+ hint: wrapped.hint ?? wrapped.recovery?.command ?? `meshy download --resource ${step.resource} --task-id ${task.id} --all --output-dir <dir>`,
247
+ details: wrapped.details,
248
+ warnings: wrapped.warnings,
249
+ result: wrapped.result,
250
+ cause: err,
251
+ });
252
+ }
253
+ return;
254
+ }
255
+ const base = buildTaskResult({ task, raw, descriptor, includeRaw: false, submission });
256
+ let downloads = base.downloads;
257
+ if (runtime.flags.output) {
258
+ try {
259
+ const { files, metadataPath, materialLinks } = await downloadArtifacts(task, runtime.flags.output, step.resource, { root: runtime.flags.workspaceRoot, signal: abortSignal() });
260
+ if (materialLinks)
261
+ warnings.push(...materialLinks.warnings);
262
+ downloads = { state: "completed", files: files.map((f) => ({ key: f.key, path: f.path, status: f.status, bytes: f.bytes, sha256: f.sha256, error: f.error })), metadata_path: metadataPath, material_links: materialLinks };
263
+ }
264
+ catch (err) {
265
+ // Whatever the downloader already committed stays in the manifest; the
266
+ // failure keeps its own class (HTTP status, interrupted, local I/O).
267
+ const partial = err instanceof CliError && err.result && typeof err.result["downloads"] === "object" ? err.result["downloads"] : { state: "failed", files: [], metadata_path: null };
268
+ const interrupted = (err instanceof CliError && err.code === "interrupted") || wasInterrupted();
269
+ throw new CliError({
270
+ code: interrupted ? "interrupted" : err instanceof CliError ? err.code : "local_io",
271
+ message: `make finished (task ${task.id}) but downloading its assets ${interrupted ? "was interrupted" : "failed"}: ${err instanceof Error ? err.message : String(err)}`,
272
+ httpStatus: err instanceof CliError ? err.httpStatus : null,
273
+ retryable: err instanceof CliError ? err.retryable : false,
274
+ recovery: err instanceof CliError && err.recovery ? err.recovery : { action: "download", automatic: false, command: `meshy download --resource ${step.resource} --task-id ${task.id} --all --output-dir <dir>` },
275
+ hint: err instanceof CliError ? err.hint : undefined,
276
+ details: err instanceof CliError ? err.details : undefined,
277
+ warnings: [...warnings, ...(err instanceof CliError ? err.warnings : [])],
278
+ result: { route: plan.route, executed, task_id: task.id, task: base.task, submission: base.submission, downloads: partial, next: taskNextCommands(descriptor, task.id) },
279
+ cause: err,
280
+ });
281
+ }
282
+ }
283
+ await emitEnvelope(okEnvelope("make", { route: plan.route, executed, task: base.task, submission: base.submission, downloads, pending_steps: [] }, warnings), opened.format);
284
+ }
100
285
  /**
101
286
  * One payload builder per step, taking the previous step's task id so the
102
287
  * chain stays a function of the plan plus what actually ran.
@@ -120,11 +305,10 @@ async function buildPayloads(plan) {
120
305
  }
121
306
  // Resolve the image before anything is created: a missing file or an
122
307
  // unreachable URL must fail while the run is still free.
123
- const resolved = { imageUrl: plan.input };
124
- await resolveImageFields(resolved);
308
+ const { payload } = await normalizeMediaPayload({ image_url: plan.input }, requireTaskResource("image-to-3d").mediaFields, { signal: abortSignal() });
125
309
  return [
126
310
  () => ({
127
- image_url: resolved.imageUrl,
311
+ image_url: payload.image_url,
128
312
  should_texture: true,
129
313
  enable_pbr: true,
130
314
  texture_resolution: "4k",
@@ -147,22 +331,41 @@ export function resumeCommand(plan, completedTaskId, runtime) {
147
331
  const output = runtime.flags.output ? ` -o ${runtime.flags.output}` : "";
148
332
  return `meshy text-to-3d create --mode refine --preview-task-id ${completedTaskId}${output}`;
149
333
  }
150
- function stepFailure(plan, step, task, timedOut, completedTaskId, runtime) {
334
+ function stepFailure(plan, step, task, taskId, raw, timedOut, completedTaskId, runtime, executed, opened) {
335
+ const descriptor = requireTaskResource(step.resource);
336
+ const taskView = task ? buildTaskResult({ task, raw, descriptor, includeRaw: false, submission: { state: "accepted", operation_id: null, task_id: taskId } }).task : null;
337
+ const resume = resumeCommand(plan, completedTaskId, runtime);
338
+ const status = task?.status ?? "unknown";
339
+ if (opened.schema === "v1") {
340
+ if (timedOut) {
341
+ return new CliError({
342
+ code: "timed_out",
343
+ message: `make: ${step.label} did not finish within the timeout — task ${taskId} is still running`,
344
+ recovery: { action: "wait", automatic: false, command: `meshy ${step.resource} wait ${taskId}` },
345
+ result: { route: plan.route, executed, task_id: taskId, task: taskView, pending_steps: pendingSteps(plan, step.index, null, runtime.flags.output), resume: resume ?? null, next: taskNextCommands(descriptor, taskId) },
346
+ });
347
+ }
348
+ return new CliError({
349
+ code: "task_failed",
350
+ message: task?.task_error?.message || `make: ${step.label} ended as ${status} — task ${taskId}`,
351
+ recovery: resume ? { action: "resume", automatic: false, command: resume } : null,
352
+ result: { route: plan.route, executed, task_id: taskId, task: taskView, pending_steps: pendingSteps(plan, step.index, completedTaskId || null, runtime.flags.output), resume: resume ?? null },
353
+ });
354
+ }
151
355
  if (timedOut) {
152
356
  return new HintedError({
153
- message: `make: ${step.label} did not finish within the timeout — task ${task.id} is still running`,
357
+ message: `make: ${step.label} did not finish within the timeout — task ${taskId} is still running`,
154
358
  code: "step_timeout",
155
- hint: `meshy ${step.resource} wait ${task.id}`,
359
+ hint: `meshy ${step.resource} wait ${taskId}`,
156
360
  exitCode: 8,
157
361
  });
158
362
  }
159
363
  // A resume is only offered when an earlier step actually succeeded; without
160
364
  // one, a suggested command would be a guess, and a wrong command is worse
161
365
  // than none.
162
- const resume = resumeCommand(plan, completedTaskId, runtime);
163
366
  return new HintedError({
164
- message: task.task_error?.message ||
165
- `make: ${step.label} ended as ${task.status} — task ${task.id}`,
367
+ message: task?.task_error?.message ||
368
+ `make: ${step.label} ended as ${status} — task ${taskId}`,
166
369
  code: "step_failed",
167
370
  ...(resume ? { hint: `${resume} # step ${step.index} failed; step ${step.index - 1} is kept` } : {}),
168
371
  });
@@ -189,7 +392,8 @@ function announceStart(plan, step, taskId) {
189
392
  process.stderr.write(`[${step.index}/${plan.steps.length}] ${step.label} ${taskId}\n`);
190
393
  }
191
394
  function announceOutcome(task, timedOut, elapsed) {
192
- const mark = timedOut ? "timed out" : task.status === "SUCCEEDED" ? "ok" : task.status;
395
+ const mark = timedOut ? "timed out" : task ? (task.status === "SUCCEEDED" ? "ok" : task.status) : "no status received";
193
396
  process.stderr.write(` ${mark} in ${elapsed.toFixed(0)}s\n`);
194
397
  }
398
+ export { warning as _makeWarning };
195
399
  //# sourceMappingURL=make.js.map