@clickraft/cli 0.3.0 → 0.4.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.
- package/CHANGELOG.md +3 -2
- package/README.md +19 -0
- package/dist/cli.js +230 -105
- package/dist/cli.js.map +1 -1
- package/dist/index.js +230 -105
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
## [0.
|
|
1
|
+
## [0.4.0](https://github.com/clickraft/cli/compare/v0.3.0...v0.4.0) (2026-05-24)
|
|
2
2
|
|
|
3
3
|
### Features
|
|
4
4
|
|
|
5
|
-
* **
|
|
5
|
+
* **errors:** add brand model error codes and surface error details ([98ee9f4](https://github.com/clickraft/cli/commit/98ee9f415a85cf31e38152092655accbe9acef1c))
|
|
6
|
+
* **generate:** make --brand-model repeatable with pose support ([872209c](https://github.com/clickraft/cli/commit/872209cfab22f7cd31e2a7655fa072c437a78351))
|
|
6
7
|
|
|
7
8
|
# Changelog
|
|
8
9
|
|
package/README.md
CHANGED
|
@@ -24,6 +24,25 @@ clickraft template get <template-id>
|
|
|
24
24
|
clickraft template find "coffee brand"
|
|
25
25
|
```
|
|
26
26
|
|
|
27
|
+
### Brand Model references
|
|
28
|
+
|
|
29
|
+
Use `--brand-model` to attach trained brand models to a generation. The flag is repeatable (max 3) and accepts an optional pose:
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
# Single brand model, primary image
|
|
33
|
+
clickraft generate create --model-slug nano-banana-2 \
|
|
34
|
+
--prompt "product on marble" \
|
|
35
|
+
--brand-model a2438521-abcd-4000-8000-000000000001
|
|
36
|
+
|
|
37
|
+
# Two brand models with pose selection
|
|
38
|
+
clickraft generate create --model-slug nano-banana-2 \
|
|
39
|
+
--prompt "lifestyle shot" \
|
|
40
|
+
--brand-model a2438521-abcd-4000-8000-000000000001:front \
|
|
41
|
+
--brand-model b3549632-bcde-4000-8000-000000000002:3/4-right
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
> **Note:** `--brand-model` only works with reference-supporting models (nano-banana-2 recommended).
|
|
45
|
+
|
|
27
46
|
Full documentation: [docs.clickraft.ai/cli](https://docs.clickraft.ai/cli) _(coming soon)_
|
|
28
47
|
|
|
29
48
|
## Connecting to staging
|
package/dist/cli.js
CHANGED
|
@@ -285,6 +285,55 @@ var EXIT_CODE_MAP = {
|
|
|
285
285
|
retryable: false,
|
|
286
286
|
hint: "No such profile. Use `clickraft auth profiles` to list available profiles."
|
|
287
287
|
},
|
|
288
|
+
// --- 8 brand model resolution codes -----------------------------------------
|
|
289
|
+
BRAND_MODEL_NOT_FOUND: {
|
|
290
|
+
exitCode: 1,
|
|
291
|
+
category: "runtime",
|
|
292
|
+
retryable: false,
|
|
293
|
+
hint: "No brand model with that ID. Run `clickraft brand-model list`."
|
|
294
|
+
},
|
|
295
|
+
BRAND_MODEL_NOT_A_MODEL: {
|
|
296
|
+
exitCode: 1,
|
|
297
|
+
category: "runtime",
|
|
298
|
+
retryable: false,
|
|
299
|
+
hint: "The specified asset is not a brand model."
|
|
300
|
+
},
|
|
301
|
+
BRAND_MODEL_NOT_ACTIVE: {
|
|
302
|
+
exitCode: 1,
|
|
303
|
+
category: "runtime",
|
|
304
|
+
retryable: false,
|
|
305
|
+
hint: "Brand model is not active. Check its status in the dashboard."
|
|
306
|
+
},
|
|
307
|
+
BRAND_MODEL_NO_REFERENCES: {
|
|
308
|
+
exitCode: 1,
|
|
309
|
+
category: "runtime",
|
|
310
|
+
retryable: false,
|
|
311
|
+
hint: "Brand model has no reference images. Upload references first."
|
|
312
|
+
},
|
|
313
|
+
BRAND_MODEL_POSE_NOT_FOUND: {
|
|
314
|
+
exitCode: 1,
|
|
315
|
+
category: "runtime",
|
|
316
|
+
retryable: false,
|
|
317
|
+
hint: "Requested pose not found. Valid: front, 3/4-right, right, left, 3/4-left, back, approved."
|
|
318
|
+
},
|
|
319
|
+
BRAND_MODEL_DUPLICATE_ID: {
|
|
320
|
+
exitCode: 2,
|
|
321
|
+
category: "usage",
|
|
322
|
+
retryable: false,
|
|
323
|
+
hint: "Same brand model ID appears more than once."
|
|
324
|
+
},
|
|
325
|
+
MODEL_DOES_NOT_SUPPORT_REFERENCES: {
|
|
326
|
+
exitCode: 2,
|
|
327
|
+
category: "usage",
|
|
328
|
+
retryable: false,
|
|
329
|
+
hint: "This model does not support brand model references."
|
|
330
|
+
},
|
|
331
|
+
REFERENCE_LIMIT_EXCEEDED: {
|
|
332
|
+
exitCode: 2,
|
|
333
|
+
category: "usage",
|
|
334
|
+
retryable: false,
|
|
335
|
+
hint: "Too many reference images. Check model limits."
|
|
336
|
+
},
|
|
288
337
|
// --- 4 CLI-synthetic codes --------------------------------------------------
|
|
289
338
|
NETWORK_ERROR: {
|
|
290
339
|
exitCode: 6,
|
|
@@ -697,7 +746,8 @@ function apiErrorFromServerBody(args) {
|
|
|
697
746
|
requestId: error.correlationId ?? args.requestId,
|
|
698
747
|
retryable,
|
|
699
748
|
retryAfterMs,
|
|
700
|
-
redactedBody: args.redactedBody
|
|
749
|
+
redactedBody: args.redactedBody,
|
|
750
|
+
details: error.details
|
|
701
751
|
});
|
|
702
752
|
}
|
|
703
753
|
function parseRetryAfterHeader(headerValue) {
|
|
@@ -826,7 +876,8 @@ function errorToEnvelopePayload(err) {
|
|
|
826
876
|
message: err.message,
|
|
827
877
|
retryable: err.meta.retryable,
|
|
828
878
|
retryAfterMs: err.meta.retryAfterMs,
|
|
829
|
-
requestId: err.meta.requestId
|
|
879
|
+
requestId: err.meta.requestId,
|
|
880
|
+
details: err.meta.details
|
|
830
881
|
});
|
|
831
882
|
}
|
|
832
883
|
if (err instanceof AuthError || err instanceof CredentialsError) {
|
|
@@ -855,6 +906,7 @@ function buildPayload(args) {
|
|
|
855
906
|
if (typeof args.retryAfterMs === "number") payload.retry_after_ms = args.retryAfterMs;
|
|
856
907
|
if (args.requestId) payload.request_id = args.requestId;
|
|
857
908
|
if (mapping?.hint) payload.hint = mapping.hint;
|
|
909
|
+
if (args.details != null) payload.details = args.details;
|
|
858
910
|
return payload;
|
|
859
911
|
}
|
|
860
912
|
|
|
@@ -1736,7 +1788,15 @@ function buildRequestBody(args) {
|
|
|
1736
1788
|
if (args.aspectRatio !== void 0) input.aspectRatio = args.aspectRatio;
|
|
1737
1789
|
if (args.resolution !== void 0) input.resolution = args.resolution;
|
|
1738
1790
|
if (args.durationSeconds !== void 0) input.durationSeconds = args.durationSeconds;
|
|
1739
|
-
if (args.
|
|
1791
|
+
if (args.brandModels !== void 0 && args.brandModels.length > 0) {
|
|
1792
|
+
input.providerParams = {
|
|
1793
|
+
brandModels: args.brandModels.map((spec) => {
|
|
1794
|
+
const entry = { id: spec.id };
|
|
1795
|
+
if (spec.imageType) entry.imageType = spec.imageType;
|
|
1796
|
+
return entry;
|
|
1797
|
+
})
|
|
1798
|
+
};
|
|
1799
|
+
}
|
|
1740
1800
|
const body = {
|
|
1741
1801
|
modelSlug: args.modelSlug,
|
|
1742
1802
|
input
|
|
@@ -1773,6 +1833,152 @@ function projectCreateToResult(response, modelSlug) {
|
|
|
1773
1833
|
};
|
|
1774
1834
|
}
|
|
1775
1835
|
|
|
1836
|
+
// src/cli/args.ts
|
|
1837
|
+
var ArgError = class extends Error {
|
|
1838
|
+
constructor(message) {
|
|
1839
|
+
super(message);
|
|
1840
|
+
this.name = "ArgError";
|
|
1841
|
+
}
|
|
1842
|
+
};
|
|
1843
|
+
function parseArgs(argv, spec) {
|
|
1844
|
+
const result = {
|
|
1845
|
+
positional: [],
|
|
1846
|
+
string: {},
|
|
1847
|
+
boolean: {},
|
|
1848
|
+
array: {}
|
|
1849
|
+
};
|
|
1850
|
+
const stringFlags = new Set(spec.string ?? []);
|
|
1851
|
+
const booleanFlags = new Set(spec.boolean ?? []);
|
|
1852
|
+
const arrayFlags = new Set(spec.array ?? []);
|
|
1853
|
+
const aliases = spec.aliases ?? {};
|
|
1854
|
+
let i = 0;
|
|
1855
|
+
let stopFlags = false;
|
|
1856
|
+
while (i < argv.length) {
|
|
1857
|
+
const raw = argv[i];
|
|
1858
|
+
if (raw === void 0) {
|
|
1859
|
+
i += 1;
|
|
1860
|
+
continue;
|
|
1861
|
+
}
|
|
1862
|
+
if (stopFlags) {
|
|
1863
|
+
result.positional.push(raw);
|
|
1864
|
+
i += 1;
|
|
1865
|
+
continue;
|
|
1866
|
+
}
|
|
1867
|
+
if (raw === "--") {
|
|
1868
|
+
stopFlags = true;
|
|
1869
|
+
i += 1;
|
|
1870
|
+
continue;
|
|
1871
|
+
}
|
|
1872
|
+
if (!raw.startsWith("--")) {
|
|
1873
|
+
result.positional.push(raw);
|
|
1874
|
+
i += 1;
|
|
1875
|
+
continue;
|
|
1876
|
+
}
|
|
1877
|
+
const eqIdx = raw.indexOf("=");
|
|
1878
|
+
const flagToken = eqIdx >= 0 ? raw.slice(2, eqIdx) : raw.slice(2);
|
|
1879
|
+
const inlineValue = eqIdx >= 0 ? raw.slice(eqIdx + 1) : void 0;
|
|
1880
|
+
const canonical = resolveCanonical(flagToken, { booleanFlags, aliases });
|
|
1881
|
+
if (booleanFlags.has(canonical)) {
|
|
1882
|
+
if (inlineValue !== void 0) {
|
|
1883
|
+
result.boolean[canonical] = parseBool(inlineValue);
|
|
1884
|
+
} else if (flagToken.startsWith("no-") && booleanFlags.has(flagToken.slice(3))) {
|
|
1885
|
+
result.boolean[flagToken.slice(3)] = false;
|
|
1886
|
+
} else {
|
|
1887
|
+
result.boolean[canonical] = true;
|
|
1888
|
+
}
|
|
1889
|
+
i += 1;
|
|
1890
|
+
continue;
|
|
1891
|
+
}
|
|
1892
|
+
if (stringFlags.has(canonical) || arrayFlags.has(canonical)) {
|
|
1893
|
+
let value;
|
|
1894
|
+
if (inlineValue !== void 0) {
|
|
1895
|
+
value = inlineValue;
|
|
1896
|
+
} else {
|
|
1897
|
+
const next = argv[i + 1];
|
|
1898
|
+
if (next === void 0 || next.startsWith("--")) {
|
|
1899
|
+
throw new ArgError(`Flag --${canonical} requires a value.`);
|
|
1900
|
+
}
|
|
1901
|
+
value = next;
|
|
1902
|
+
i += 1;
|
|
1903
|
+
}
|
|
1904
|
+
if (arrayFlags.has(canonical)) {
|
|
1905
|
+
const existing = result.array[canonical] ?? [];
|
|
1906
|
+
existing.push(value);
|
|
1907
|
+
result.array[canonical] = existing;
|
|
1908
|
+
} else {
|
|
1909
|
+
result.string[canonical] = value;
|
|
1910
|
+
}
|
|
1911
|
+
i += 1;
|
|
1912
|
+
continue;
|
|
1913
|
+
}
|
|
1914
|
+
throw new ArgError(`Unknown flag --${flagToken}`);
|
|
1915
|
+
}
|
|
1916
|
+
return result;
|
|
1917
|
+
}
|
|
1918
|
+
function resolveCanonical(flagToken, ctx) {
|
|
1919
|
+
if (ctx.aliases[flagToken]) return ctx.aliases[flagToken];
|
|
1920
|
+
if (flagToken.startsWith("no-") && ctx.booleanFlags.has(flagToken.slice(3))) {
|
|
1921
|
+
return flagToken.slice(3);
|
|
1922
|
+
}
|
|
1923
|
+
return flagToken;
|
|
1924
|
+
}
|
|
1925
|
+
function parseBool(value) {
|
|
1926
|
+
if (value === "true" || value === "1") return true;
|
|
1927
|
+
if (value === "false" || value === "0") return false;
|
|
1928
|
+
throw new ArgError(`Expected boolean value (true/false), got "${value}".`);
|
|
1929
|
+
}
|
|
1930
|
+
|
|
1931
|
+
// src/commands/generate/parse-brand-model-spec.ts
|
|
1932
|
+
var VALID_POSES = [
|
|
1933
|
+
"front",
|
|
1934
|
+
"3/4-right",
|
|
1935
|
+
"right",
|
|
1936
|
+
"left",
|
|
1937
|
+
"3/4-left",
|
|
1938
|
+
"back",
|
|
1939
|
+
"approved"
|
|
1940
|
+
];
|
|
1941
|
+
var UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
1942
|
+
function parseBrandModelSpec(raw) {
|
|
1943
|
+
if (!raw) throw new ArgError("--brand-model value cannot be empty.");
|
|
1944
|
+
const colonIdx = raw.indexOf(":");
|
|
1945
|
+
const uuid = colonIdx >= 0 ? raw.slice(0, colonIdx) : raw;
|
|
1946
|
+
const poseRaw = colonIdx >= 0 ? raw.slice(colonIdx + 1) : void 0;
|
|
1947
|
+
if (!UUID_RE.test(uuid)) {
|
|
1948
|
+
throw new ArgError(
|
|
1949
|
+
`Invalid brand model ID: '${uuid}'. Expected a UUID (xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx).`
|
|
1950
|
+
);
|
|
1951
|
+
}
|
|
1952
|
+
if (poseRaw !== void 0) {
|
|
1953
|
+
if (!poseRaw) {
|
|
1954
|
+
throw new ArgError(
|
|
1955
|
+
`Empty pose for brand model ${uuid}. Valid poses: ${VALID_POSES.join(", ")}.`
|
|
1956
|
+
);
|
|
1957
|
+
}
|
|
1958
|
+
if (!VALID_POSES.includes(poseRaw)) {
|
|
1959
|
+
throw new ArgError(
|
|
1960
|
+
`Unknown pose '${poseRaw}' for brand model ${uuid}. Valid poses: ${VALID_POSES.join(", ")}.`
|
|
1961
|
+
);
|
|
1962
|
+
}
|
|
1963
|
+
return { id: uuid.toLowerCase(), imageType: poseRaw };
|
|
1964
|
+
}
|
|
1965
|
+
return { id: uuid.toLowerCase() };
|
|
1966
|
+
}
|
|
1967
|
+
function validateBrandModelSpecs(specs) {
|
|
1968
|
+
if (specs.length > 3) {
|
|
1969
|
+
throw new ArgError(`Too many --brand-model flags (${specs.length}). Maximum is 3.`);
|
|
1970
|
+
}
|
|
1971
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1972
|
+
for (const spec of specs) {
|
|
1973
|
+
if (seen.has(spec.id)) {
|
|
1974
|
+
throw new ArgError(
|
|
1975
|
+
`Duplicate brand model ID: ${spec.id}. Each --brand-model must reference a different model.`
|
|
1976
|
+
);
|
|
1977
|
+
}
|
|
1978
|
+
seen.add(spec.id);
|
|
1979
|
+
}
|
|
1980
|
+
}
|
|
1981
|
+
|
|
1776
1982
|
// src/auth/device-flow.ts
|
|
1777
1983
|
import { setTimeout as delay } from "timers/promises";
|
|
1778
1984
|
import { request } from "undici";
|
|
@@ -2443,23 +2649,33 @@ async function listTokens(options) {
|
|
|
2443
2649
|
var FALLBACK_HINT = "See `clickraft --help` or contact support.";
|
|
2444
2650
|
function formatHumanError(err) {
|
|
2445
2651
|
const view = errorToView(err);
|
|
2446
|
-
|
|
2652
|
+
const lines = [
|
|
2447
2653
|
`Error: ${view.message}`,
|
|
2448
2654
|
`Code: ${view.code}`,
|
|
2449
2655
|
`Try: ${view.hint}`,
|
|
2450
2656
|
`Request ID: ${view.requestId ?? "n/a"}`
|
|
2451
|
-
]
|
|
2657
|
+
];
|
|
2658
|
+
if (view.details) lines.push(`Details: ${view.details}`);
|
|
2659
|
+
return lines.join("\n");
|
|
2452
2660
|
}
|
|
2453
2661
|
function formatJsonError(err, ctx) {
|
|
2454
2662
|
return JSON.stringify(wrapError(err, ctx));
|
|
2455
2663
|
}
|
|
2664
|
+
function formatDetails(details) {
|
|
2665
|
+
if (details == null) return void 0;
|
|
2666
|
+
if (typeof details !== "object") return String(details);
|
|
2667
|
+
const entries = Object.entries(details);
|
|
2668
|
+
if (entries.length === 0) return void 0;
|
|
2669
|
+
return entries.map(([k, v]) => `${k}=${v}`).join(", ");
|
|
2670
|
+
}
|
|
2456
2671
|
function errorToView(err) {
|
|
2457
2672
|
if (err instanceof ApiError) {
|
|
2458
2673
|
return {
|
|
2459
2674
|
code: toEnvelopeCode(err.code),
|
|
2460
2675
|
message: err.message,
|
|
2461
2676
|
hint: EXIT_CODE_MAP[err.code]?.hint ?? FALLBACK_HINT,
|
|
2462
|
-
requestId: err.meta.requestId
|
|
2677
|
+
requestId: err.meta.requestId,
|
|
2678
|
+
details: formatDetails(err.meta.details)
|
|
2463
2679
|
};
|
|
2464
2680
|
}
|
|
2465
2681
|
if (err instanceof AuthError || err instanceof CredentialsError) {
|
|
@@ -2477,101 +2693,6 @@ function errorToView(err) {
|
|
|
2477
2693
|
};
|
|
2478
2694
|
}
|
|
2479
2695
|
|
|
2480
|
-
// src/cli/args.ts
|
|
2481
|
-
var ArgError = class extends Error {
|
|
2482
|
-
constructor(message) {
|
|
2483
|
-
super(message);
|
|
2484
|
-
this.name = "ArgError";
|
|
2485
|
-
}
|
|
2486
|
-
};
|
|
2487
|
-
function parseArgs(argv, spec) {
|
|
2488
|
-
const result = {
|
|
2489
|
-
positional: [],
|
|
2490
|
-
string: {},
|
|
2491
|
-
boolean: {},
|
|
2492
|
-
array: {}
|
|
2493
|
-
};
|
|
2494
|
-
const stringFlags = new Set(spec.string ?? []);
|
|
2495
|
-
const booleanFlags = new Set(spec.boolean ?? []);
|
|
2496
|
-
const arrayFlags = new Set(spec.array ?? []);
|
|
2497
|
-
const aliases = spec.aliases ?? {};
|
|
2498
|
-
let i = 0;
|
|
2499
|
-
let stopFlags = false;
|
|
2500
|
-
while (i < argv.length) {
|
|
2501
|
-
const raw = argv[i];
|
|
2502
|
-
if (raw === void 0) {
|
|
2503
|
-
i += 1;
|
|
2504
|
-
continue;
|
|
2505
|
-
}
|
|
2506
|
-
if (stopFlags) {
|
|
2507
|
-
result.positional.push(raw);
|
|
2508
|
-
i += 1;
|
|
2509
|
-
continue;
|
|
2510
|
-
}
|
|
2511
|
-
if (raw === "--") {
|
|
2512
|
-
stopFlags = true;
|
|
2513
|
-
i += 1;
|
|
2514
|
-
continue;
|
|
2515
|
-
}
|
|
2516
|
-
if (!raw.startsWith("--")) {
|
|
2517
|
-
result.positional.push(raw);
|
|
2518
|
-
i += 1;
|
|
2519
|
-
continue;
|
|
2520
|
-
}
|
|
2521
|
-
const eqIdx = raw.indexOf("=");
|
|
2522
|
-
const flagToken = eqIdx >= 0 ? raw.slice(2, eqIdx) : raw.slice(2);
|
|
2523
|
-
const inlineValue = eqIdx >= 0 ? raw.slice(eqIdx + 1) : void 0;
|
|
2524
|
-
const canonical = resolveCanonical(flagToken, { booleanFlags, aliases });
|
|
2525
|
-
if (booleanFlags.has(canonical)) {
|
|
2526
|
-
if (inlineValue !== void 0) {
|
|
2527
|
-
result.boolean[canonical] = parseBool(inlineValue);
|
|
2528
|
-
} else if (flagToken.startsWith("no-") && booleanFlags.has(flagToken.slice(3))) {
|
|
2529
|
-
result.boolean[flagToken.slice(3)] = false;
|
|
2530
|
-
} else {
|
|
2531
|
-
result.boolean[canonical] = true;
|
|
2532
|
-
}
|
|
2533
|
-
i += 1;
|
|
2534
|
-
continue;
|
|
2535
|
-
}
|
|
2536
|
-
if (stringFlags.has(canonical) || arrayFlags.has(canonical)) {
|
|
2537
|
-
let value;
|
|
2538
|
-
if (inlineValue !== void 0) {
|
|
2539
|
-
value = inlineValue;
|
|
2540
|
-
} else {
|
|
2541
|
-
const next = argv[i + 1];
|
|
2542
|
-
if (next === void 0 || next.startsWith("--")) {
|
|
2543
|
-
throw new ArgError(`Flag --${canonical} requires a value.`);
|
|
2544
|
-
}
|
|
2545
|
-
value = next;
|
|
2546
|
-
i += 1;
|
|
2547
|
-
}
|
|
2548
|
-
if (arrayFlags.has(canonical)) {
|
|
2549
|
-
const existing = result.array[canonical] ?? [];
|
|
2550
|
-
existing.push(value);
|
|
2551
|
-
result.array[canonical] = existing;
|
|
2552
|
-
} else {
|
|
2553
|
-
result.string[canonical] = value;
|
|
2554
|
-
}
|
|
2555
|
-
i += 1;
|
|
2556
|
-
continue;
|
|
2557
|
-
}
|
|
2558
|
-
throw new ArgError(`Unknown flag --${flagToken}`);
|
|
2559
|
-
}
|
|
2560
|
-
return result;
|
|
2561
|
-
}
|
|
2562
|
-
function resolveCanonical(flagToken, ctx) {
|
|
2563
|
-
if (ctx.aliases[flagToken]) return ctx.aliases[flagToken];
|
|
2564
|
-
if (flagToken.startsWith("no-") && ctx.booleanFlags.has(flagToken.slice(3))) {
|
|
2565
|
-
return flagToken.slice(3);
|
|
2566
|
-
}
|
|
2567
|
-
return flagToken;
|
|
2568
|
-
}
|
|
2569
|
-
function parseBool(value) {
|
|
2570
|
-
if (value === "true" || value === "1") return true;
|
|
2571
|
-
if (value === "false" || value === "0") return false;
|
|
2572
|
-
throw new ArgError(`Expected boolean value (true/false), got "${value}".`);
|
|
2573
|
-
}
|
|
2574
|
-
|
|
2575
2696
|
// src/cli/parse-args.ts
|
|
2576
2697
|
var SHORT_ALIASES = {
|
|
2577
2698
|
h: "help",
|
|
@@ -3256,7 +3377,9 @@ function renderCommandHelp(command) {
|
|
|
3256
3377
|
"Options:",
|
|
3257
3378
|
" --prompt <text> Text prompt (or pass as the positional argument)",
|
|
3258
3379
|
" --model-slug <slug> Required model identifier",
|
|
3259
|
-
" --brand-model <
|
|
3380
|
+
" --brand-model <spec> Brand Model reference (repeatable, max 3)",
|
|
3381
|
+
" Format: <uuid> or <uuid>:<pose>",
|
|
3382
|
+
" Poses: front, 3/4-right, right, left, 3/4-left, back, approved",
|
|
3260
3383
|
" --reference-image <v> URL or local path (repeatable, max 8)",
|
|
3261
3384
|
" --aspect-ratio <a:b> e.g. 16:9",
|
|
3262
3385
|
" --resolution <WxH> e.g. 1024x768",
|
|
@@ -3451,7 +3574,6 @@ async function runGenerateCreate(argv, ctx, rc) {
|
|
|
3451
3574
|
...COMMON_RICH_STRING_FLAGS,
|
|
3452
3575
|
"prompt",
|
|
3453
3576
|
"model-slug",
|
|
3454
|
-
"brand-model",
|
|
3455
3577
|
"aspect-ratio",
|
|
3456
3578
|
"resolution",
|
|
3457
3579
|
"duration-seconds",
|
|
@@ -3460,10 +3582,13 @@ async function runGenerateCreate(argv, ctx, rc) {
|
|
|
3460
3582
|
"timeout"
|
|
3461
3583
|
],
|
|
3462
3584
|
boolean: [...COMMON_RICH_BOOL_FLAGS, "wait", "async"],
|
|
3463
|
-
array: ["reference-image"]
|
|
3585
|
+
array: ["reference-image", "brand-model"]
|
|
3464
3586
|
});
|
|
3465
3587
|
const modelSlug = parsed.string["model-slug"];
|
|
3466
3588
|
if (!modelSlug) throw new ArgError("--model-slug is required.");
|
|
3589
|
+
const rawBrandModels = parsed.array["brand-model"] ?? [];
|
|
3590
|
+
const brandModels = rawBrandModels.length > 0 ? rawBrandModels.map(parseBrandModelSpec) : void 0;
|
|
3591
|
+
if (brandModels) validateBrandModelSpecs(brandModels);
|
|
3467
3592
|
const result = await generateCreate({
|
|
3468
3593
|
prompt: parsed.string.prompt ?? parsed.positional[0],
|
|
3469
3594
|
modelSlug,
|
|
@@ -3471,7 +3596,7 @@ async function runGenerateCreate(argv, ctx, rc) {
|
|
|
3471
3596
|
aspectRatio: parsed.string["aspect-ratio"],
|
|
3472
3597
|
resolution: parsed.string.resolution,
|
|
3473
3598
|
durationSeconds: optionalInt(parsed.string["duration-seconds"], "duration-seconds"),
|
|
3474
|
-
|
|
3599
|
+
brandModels,
|
|
3475
3600
|
workflowId: parsed.string["workflow-id"],
|
|
3476
3601
|
nodeId: parsed.string["node-id"],
|
|
3477
3602
|
wait: computeWaitFlag(parsed),
|