@clickraft/cli 0.2.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 +22 -0
- package/dist/cli.js +545 -116
- package/dist/cli.js.map +1 -1
- package/dist/index.js +545 -116
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
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) {
|
|
@@ -721,6 +771,10 @@ function buildUrl(baseUrl, path, query) {
|
|
|
721
771
|
if (query) {
|
|
722
772
|
for (const [key, value] of Object.entries(query)) {
|
|
723
773
|
if (value === void 0) continue;
|
|
774
|
+
if (Array.isArray(value)) {
|
|
775
|
+
for (const item of value) url.searchParams.append(key, item);
|
|
776
|
+
continue;
|
|
777
|
+
}
|
|
724
778
|
url.searchParams.append(key, String(value));
|
|
725
779
|
}
|
|
726
780
|
}
|
|
@@ -822,7 +876,8 @@ function errorToEnvelopePayload(err) {
|
|
|
822
876
|
message: err.message,
|
|
823
877
|
retryable: err.meta.retryable,
|
|
824
878
|
retryAfterMs: err.meta.retryAfterMs,
|
|
825
|
-
requestId: err.meta.requestId
|
|
879
|
+
requestId: err.meta.requestId,
|
|
880
|
+
details: err.meta.details
|
|
826
881
|
});
|
|
827
882
|
}
|
|
828
883
|
if (err instanceof AuthError || err instanceof CredentialsError) {
|
|
@@ -851,6 +906,7 @@ function buildPayload(args) {
|
|
|
851
906
|
if (typeof args.retryAfterMs === "number") payload.retry_after_ms = args.retryAfterMs;
|
|
852
907
|
if (args.requestId) payload.request_id = args.requestId;
|
|
853
908
|
if (mapping?.hint) payload.hint = mapping.hint;
|
|
909
|
+
if (args.details != null) payload.details = args.details;
|
|
854
910
|
return payload;
|
|
855
911
|
}
|
|
856
912
|
|
|
@@ -1732,7 +1788,15 @@ function buildRequestBody(args) {
|
|
|
1732
1788
|
if (args.aspectRatio !== void 0) input.aspectRatio = args.aspectRatio;
|
|
1733
1789
|
if (args.resolution !== void 0) input.resolution = args.resolution;
|
|
1734
1790
|
if (args.durationSeconds !== void 0) input.durationSeconds = args.durationSeconds;
|
|
1735
|
-
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
|
+
}
|
|
1736
1800
|
const body = {
|
|
1737
1801
|
modelSlug: args.modelSlug,
|
|
1738
1802
|
input
|
|
@@ -1769,6 +1833,152 @@ function projectCreateToResult(response, modelSlug) {
|
|
|
1769
1833
|
};
|
|
1770
1834
|
}
|
|
1771
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
|
+
|
|
1772
1982
|
// src/auth/device-flow.ts
|
|
1773
1983
|
import { setTimeout as delay } from "timers/promises";
|
|
1774
1984
|
import { request } from "undici";
|
|
@@ -2305,21 +2515,112 @@ function inspectTelemetry(options = {}) {
|
|
|
2305
2515
|
};
|
|
2306
2516
|
}
|
|
2307
2517
|
|
|
2308
|
-
// src/commands/
|
|
2518
|
+
// src/commands/template/internal.ts
|
|
2519
|
+
async function buildTemplateClient(opts) {
|
|
2520
|
+
const cfg = await loadConfig({
|
|
2521
|
+
flagProfile: opts.profile,
|
|
2522
|
+
flagApiBaseUrl: opts.apiBaseUrl,
|
|
2523
|
+
flagToken: opts.token
|
|
2524
|
+
});
|
|
2525
|
+
return new ApiClient({
|
|
2526
|
+
apiBaseUrl: cfg.apiBaseUrl,
|
|
2527
|
+
accessToken: cfg.accessToken,
|
|
2528
|
+
signal: opts.signal,
|
|
2529
|
+
cloudflareAccess: cfg.cloudflareAccess
|
|
2530
|
+
});
|
|
2531
|
+
}
|
|
2532
|
+
|
|
2533
|
+
// src/schemas/templates.ts
|
|
2309
2534
|
import { z as z8 } from "zod";
|
|
2310
|
-
var
|
|
2535
|
+
var DeclaredParamSchema = z8.object({
|
|
2536
|
+
name: z8.string(),
|
|
2537
|
+
type: z8.string(),
|
|
2538
|
+
required: z8.boolean(),
|
|
2539
|
+
defaultValue: z8.string().nullable().optional(),
|
|
2540
|
+
enumValues: z8.array(z8.string()).nullable().optional(),
|
|
2541
|
+
description: z8.string().nullable().optional()
|
|
2542
|
+
}).strict();
|
|
2543
|
+
var TemplateSummarySchema = z8.object({
|
|
2311
2544
|
id: z8.string().uuid(),
|
|
2312
|
-
|
|
2313
|
-
|
|
2314
|
-
|
|
2315
|
-
|
|
2316
|
-
|
|
2317
|
-
|
|
2318
|
-
lastUsedAt: z8.string().nullable(),
|
|
2319
|
-
expiresAt: z8.string(),
|
|
2545
|
+
source: z8.enum(["own", "system"]),
|
|
2546
|
+
name: z8.string(),
|
|
2547
|
+
description: z8.string().nullable(),
|
|
2548
|
+
category: z8.string().nullable(),
|
|
2549
|
+
thumbnailUrl: z8.string().nullable(),
|
|
2550
|
+
tags: z8.array(z8.string()),
|
|
2320
2551
|
createdAt: z8.string()
|
|
2321
2552
|
}).strict();
|
|
2322
|
-
var
|
|
2553
|
+
var TemplateDetailSchema = TemplateSummarySchema.extend({
|
|
2554
|
+
declaredParams: z8.array(DeclaredParamSchema),
|
|
2555
|
+
timesUsed: z8.number()
|
|
2556
|
+
}).strict();
|
|
2557
|
+
var TemplatesListResponseSchema = z8.object({
|
|
2558
|
+
templates: z8.array(TemplateSummarySchema),
|
|
2559
|
+
cursor: z8.string().nullable(),
|
|
2560
|
+
hasMore: z8.boolean()
|
|
2561
|
+
}).strict();
|
|
2562
|
+
|
|
2563
|
+
// src/commands/template/list.ts
|
|
2564
|
+
async function listTemplates(opts) {
|
|
2565
|
+
const client = opts.client ?? await buildTemplateClient(opts);
|
|
2566
|
+
const data = await client.request({
|
|
2567
|
+
method: "GET",
|
|
2568
|
+
path: "/templates",
|
|
2569
|
+
query: {
|
|
2570
|
+
q: opts.q,
|
|
2571
|
+
tags: opts.tags,
|
|
2572
|
+
scope: opts.scope,
|
|
2573
|
+
cursor: opts.cursor,
|
|
2574
|
+
limit: opts.limit
|
|
2575
|
+
},
|
|
2576
|
+
responseSchema: TemplatesListResponseSchema,
|
|
2577
|
+
signal: opts.signal
|
|
2578
|
+
});
|
|
2579
|
+
return data;
|
|
2580
|
+
}
|
|
2581
|
+
|
|
2582
|
+
// src/commands/template/find.ts
|
|
2583
|
+
async function findTemplates(opts) {
|
|
2584
|
+
return listTemplates({
|
|
2585
|
+
q: opts.query,
|
|
2586
|
+
tags: opts.tags,
|
|
2587
|
+
scope: opts.scope,
|
|
2588
|
+
limit: opts.limit,
|
|
2589
|
+
cursor: opts.cursor,
|
|
2590
|
+
profile: opts.profile,
|
|
2591
|
+
apiBaseUrl: opts.apiBaseUrl,
|
|
2592
|
+
token: opts.token,
|
|
2593
|
+
signal: opts.signal,
|
|
2594
|
+
client: opts.client
|
|
2595
|
+
});
|
|
2596
|
+
}
|
|
2597
|
+
|
|
2598
|
+
// src/commands/template/get.ts
|
|
2599
|
+
async function getTemplate(opts) {
|
|
2600
|
+
const client = opts.client ?? await buildTemplateClient(opts);
|
|
2601
|
+
return client.request({
|
|
2602
|
+
method: "GET",
|
|
2603
|
+
path: `/templates/${encodeURIComponent(opts.id)}`,
|
|
2604
|
+
responseSchema: TemplateDetailSchema,
|
|
2605
|
+
signal: opts.signal
|
|
2606
|
+
});
|
|
2607
|
+
}
|
|
2608
|
+
|
|
2609
|
+
// src/commands/tokens/list.ts
|
|
2610
|
+
import { z as z9 } from "zod";
|
|
2611
|
+
var TokenRowSchema = z9.object({
|
|
2612
|
+
id: z9.string().uuid(),
|
|
2613
|
+
organizationId: z9.string().uuid(),
|
|
2614
|
+
userId: z9.string().uuid(),
|
|
2615
|
+
clientId: z9.string().nullable(),
|
|
2616
|
+
tokenPrefix: z9.string(),
|
|
2617
|
+
name: z9.string().nullable(),
|
|
2618
|
+
scopes: z9.array(z9.string()),
|
|
2619
|
+
lastUsedAt: z9.string().nullable(),
|
|
2620
|
+
expiresAt: z9.string(),
|
|
2621
|
+
createdAt: z9.string()
|
|
2622
|
+
}).strict();
|
|
2623
|
+
var TokensListResponseSchema = z9.array(TokenRowSchema);
|
|
2323
2624
|
async function listTokens(options) {
|
|
2324
2625
|
const config = await loadConfig({
|
|
2325
2626
|
flagProfile: options.profile,
|
|
@@ -2348,23 +2649,33 @@ async function listTokens(options) {
|
|
|
2348
2649
|
var FALLBACK_HINT = "See `clickraft --help` or contact support.";
|
|
2349
2650
|
function formatHumanError(err) {
|
|
2350
2651
|
const view = errorToView(err);
|
|
2351
|
-
|
|
2652
|
+
const lines = [
|
|
2352
2653
|
`Error: ${view.message}`,
|
|
2353
2654
|
`Code: ${view.code}`,
|
|
2354
2655
|
`Try: ${view.hint}`,
|
|
2355
2656
|
`Request ID: ${view.requestId ?? "n/a"}`
|
|
2356
|
-
]
|
|
2657
|
+
];
|
|
2658
|
+
if (view.details) lines.push(`Details: ${view.details}`);
|
|
2659
|
+
return lines.join("\n");
|
|
2357
2660
|
}
|
|
2358
2661
|
function formatJsonError(err, ctx) {
|
|
2359
2662
|
return JSON.stringify(wrapError(err, ctx));
|
|
2360
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
|
+
}
|
|
2361
2671
|
function errorToView(err) {
|
|
2362
2672
|
if (err instanceof ApiError) {
|
|
2363
2673
|
return {
|
|
2364
2674
|
code: toEnvelopeCode(err.code),
|
|
2365
2675
|
message: err.message,
|
|
2366
2676
|
hint: EXIT_CODE_MAP[err.code]?.hint ?? FALLBACK_HINT,
|
|
2367
|
-
requestId: err.meta.requestId
|
|
2677
|
+
requestId: err.meta.requestId,
|
|
2678
|
+
details: formatDetails(err.meta.details)
|
|
2368
2679
|
};
|
|
2369
2680
|
}
|
|
2370
2681
|
if (err instanceof AuthError || err instanceof CredentialsError) {
|
|
@@ -2382,101 +2693,6 @@ function errorToView(err) {
|
|
|
2382
2693
|
};
|
|
2383
2694
|
}
|
|
2384
2695
|
|
|
2385
|
-
// src/cli/args.ts
|
|
2386
|
-
var ArgError = class extends Error {
|
|
2387
|
-
constructor(message) {
|
|
2388
|
-
super(message);
|
|
2389
|
-
this.name = "ArgError";
|
|
2390
|
-
}
|
|
2391
|
-
};
|
|
2392
|
-
function parseArgs(argv, spec) {
|
|
2393
|
-
const result = {
|
|
2394
|
-
positional: [],
|
|
2395
|
-
string: {},
|
|
2396
|
-
boolean: {},
|
|
2397
|
-
array: {}
|
|
2398
|
-
};
|
|
2399
|
-
const stringFlags = new Set(spec.string ?? []);
|
|
2400
|
-
const booleanFlags = new Set(spec.boolean ?? []);
|
|
2401
|
-
const arrayFlags = new Set(spec.array ?? []);
|
|
2402
|
-
const aliases = spec.aliases ?? {};
|
|
2403
|
-
let i = 0;
|
|
2404
|
-
let stopFlags = false;
|
|
2405
|
-
while (i < argv.length) {
|
|
2406
|
-
const raw = argv[i];
|
|
2407
|
-
if (raw === void 0) {
|
|
2408
|
-
i += 1;
|
|
2409
|
-
continue;
|
|
2410
|
-
}
|
|
2411
|
-
if (stopFlags) {
|
|
2412
|
-
result.positional.push(raw);
|
|
2413
|
-
i += 1;
|
|
2414
|
-
continue;
|
|
2415
|
-
}
|
|
2416
|
-
if (raw === "--") {
|
|
2417
|
-
stopFlags = true;
|
|
2418
|
-
i += 1;
|
|
2419
|
-
continue;
|
|
2420
|
-
}
|
|
2421
|
-
if (!raw.startsWith("--")) {
|
|
2422
|
-
result.positional.push(raw);
|
|
2423
|
-
i += 1;
|
|
2424
|
-
continue;
|
|
2425
|
-
}
|
|
2426
|
-
const eqIdx = raw.indexOf("=");
|
|
2427
|
-
const flagToken = eqIdx >= 0 ? raw.slice(2, eqIdx) : raw.slice(2);
|
|
2428
|
-
const inlineValue = eqIdx >= 0 ? raw.slice(eqIdx + 1) : void 0;
|
|
2429
|
-
const canonical = resolveCanonical(flagToken, { booleanFlags, aliases });
|
|
2430
|
-
if (booleanFlags.has(canonical)) {
|
|
2431
|
-
if (inlineValue !== void 0) {
|
|
2432
|
-
result.boolean[canonical] = parseBool(inlineValue);
|
|
2433
|
-
} else if (flagToken.startsWith("no-") && booleanFlags.has(flagToken.slice(3))) {
|
|
2434
|
-
result.boolean[flagToken.slice(3)] = false;
|
|
2435
|
-
} else {
|
|
2436
|
-
result.boolean[canonical] = true;
|
|
2437
|
-
}
|
|
2438
|
-
i += 1;
|
|
2439
|
-
continue;
|
|
2440
|
-
}
|
|
2441
|
-
if (stringFlags.has(canonical) || arrayFlags.has(canonical)) {
|
|
2442
|
-
let value;
|
|
2443
|
-
if (inlineValue !== void 0) {
|
|
2444
|
-
value = inlineValue;
|
|
2445
|
-
} else {
|
|
2446
|
-
const next = argv[i + 1];
|
|
2447
|
-
if (next === void 0 || next.startsWith("--")) {
|
|
2448
|
-
throw new ArgError(`Flag --${canonical} requires a value.`);
|
|
2449
|
-
}
|
|
2450
|
-
value = next;
|
|
2451
|
-
i += 1;
|
|
2452
|
-
}
|
|
2453
|
-
if (arrayFlags.has(canonical)) {
|
|
2454
|
-
const existing = result.array[canonical] ?? [];
|
|
2455
|
-
existing.push(value);
|
|
2456
|
-
result.array[canonical] = existing;
|
|
2457
|
-
} else {
|
|
2458
|
-
result.string[canonical] = value;
|
|
2459
|
-
}
|
|
2460
|
-
i += 1;
|
|
2461
|
-
continue;
|
|
2462
|
-
}
|
|
2463
|
-
throw new ArgError(`Unknown flag --${flagToken}`);
|
|
2464
|
-
}
|
|
2465
|
-
return result;
|
|
2466
|
-
}
|
|
2467
|
-
function resolveCanonical(flagToken, ctx) {
|
|
2468
|
-
if (ctx.aliases[flagToken]) return ctx.aliases[flagToken];
|
|
2469
|
-
if (flagToken.startsWith("no-") && ctx.booleanFlags.has(flagToken.slice(3))) {
|
|
2470
|
-
return flagToken.slice(3);
|
|
2471
|
-
}
|
|
2472
|
-
return flagToken;
|
|
2473
|
-
}
|
|
2474
|
-
function parseBool(value) {
|
|
2475
|
-
if (value === "true" || value === "1") return true;
|
|
2476
|
-
if (value === "false" || value === "0") return false;
|
|
2477
|
-
throw new ArgError(`Expected boolean value (true/false), got "${value}".`);
|
|
2478
|
-
}
|
|
2479
|
-
|
|
2480
2696
|
// src/cli/parse-args.ts
|
|
2481
2697
|
var SHORT_ALIASES = {
|
|
2482
2698
|
h: "help",
|
|
@@ -2717,6 +2933,61 @@ function renderBrandModelsList(models, out) {
|
|
|
2717
2933
|
out.write(padRow(headers) + "\n");
|
|
2718
2934
|
for (const cells of data) out.write(padRow(cells) + "\n");
|
|
2719
2935
|
}
|
|
2936
|
+
function renderTemplatesList(result, out) {
|
|
2937
|
+
const { templates } = result;
|
|
2938
|
+
if (templates.length === 0) {
|
|
2939
|
+
out.write("No templates.\n");
|
|
2940
|
+
return;
|
|
2941
|
+
}
|
|
2942
|
+
const MAX_TAGS = 3;
|
|
2943
|
+
const headers = ["SOURCE", "NAME", "CATEGORY", "TAGS", "CREATED"];
|
|
2944
|
+
const data = templates.map((t) => {
|
|
2945
|
+
const shown = t.tags.slice(0, MAX_TAGS).join(", ");
|
|
2946
|
+
const extra = t.tags.length > MAX_TAGS ? ` +${t.tags.length - MAX_TAGS} more` : "";
|
|
2947
|
+
return [
|
|
2948
|
+
t.source,
|
|
2949
|
+
t.name,
|
|
2950
|
+
t.category ?? "\u2014",
|
|
2951
|
+
t.tags.length > 0 ? shown + extra : "\u2014",
|
|
2952
|
+
t.createdAt.slice(0, 10)
|
|
2953
|
+
];
|
|
2954
|
+
});
|
|
2955
|
+
const widths = headers.map(
|
|
2956
|
+
(header, i) => Math.max(header.length, ...data.map((cells) => cells[i].length))
|
|
2957
|
+
);
|
|
2958
|
+
const padRow = (cells) => cells.map((cell, i) => cell.padEnd(widths[i])).join(" ").trimEnd();
|
|
2959
|
+
out.write(padRow(headers) + "\n");
|
|
2960
|
+
for (const cells of data) out.write(padRow(cells) + "\n");
|
|
2961
|
+
if (result.hasMore) {
|
|
2962
|
+
out.write("\nMore results available. Use --cursor to fetch the next page.\n");
|
|
2963
|
+
}
|
|
2964
|
+
}
|
|
2965
|
+
function renderTemplateDetail(detail, out) {
|
|
2966
|
+
const pad = 17;
|
|
2967
|
+
const lines = [
|
|
2968
|
+
detail.name,
|
|
2969
|
+
`${"ID:".padEnd(pad)}${detail.id}`,
|
|
2970
|
+
`${"Source:".padEnd(pad)}${detail.source}`,
|
|
2971
|
+
`${"Category:".padEnd(pad)}${detail.category ?? "\u2014"}`,
|
|
2972
|
+
`${"Tags:".padEnd(pad)}${detail.tags.length > 0 ? detail.tags.join(", ") : "\u2014"}`,
|
|
2973
|
+
`${"Created:".padEnd(pad)}${detail.createdAt.slice(0, 10)}`,
|
|
2974
|
+
`${"Times used:".padEnd(pad)}${detail.timesUsed}`
|
|
2975
|
+
];
|
|
2976
|
+
if (detail.description) {
|
|
2977
|
+
lines.push("", detail.description);
|
|
2978
|
+
}
|
|
2979
|
+
lines.push("", "Declared parameters:");
|
|
2980
|
+
if (detail.declaredParams.length === 0) {
|
|
2981
|
+
lines.push(" (none)");
|
|
2982
|
+
} else {
|
|
2983
|
+
for (const p of detail.declaredParams) {
|
|
2984
|
+
const req = p.required ? "required" : "optional";
|
|
2985
|
+
const desc = p.description ? `: ${p.description}` : "";
|
|
2986
|
+
lines.push(` - ${p.name} (${p.type}, ${req})${desc}`);
|
|
2987
|
+
}
|
|
2988
|
+
}
|
|
2989
|
+
out.write(lines.join("\n") + "\n");
|
|
2990
|
+
}
|
|
2720
2991
|
function formatExpiresAt(iso) {
|
|
2721
2992
|
const ms = Date.parse(iso);
|
|
2722
2993
|
if (Number.isNaN(ms)) return iso;
|
|
@@ -2944,6 +3215,9 @@ function renderRootHelp(version) {
|
|
|
2944
3215
|
" balance Show credit balance and rate limits",
|
|
2945
3216
|
" brand-model list List available brand models",
|
|
2946
3217
|
" telemetry inspect Show current telemetry state and payload shape",
|
|
3218
|
+
" template list List available templates",
|
|
3219
|
+
" template get Fetch a template by ID",
|
|
3220
|
+
" template find Search templates by keyword",
|
|
2947
3221
|
" generate create Submit a generation job and wait for completion",
|
|
2948
3222
|
" generate wait Wait for an existing job to reach a terminal state",
|
|
2949
3223
|
" generate get Fetch the current state of a job",
|
|
@@ -3027,6 +3301,62 @@ function renderCommandHelp(command) {
|
|
|
3027
3301
|
" --json Emit JSON envelope instead of human output",
|
|
3028
3302
|
""
|
|
3029
3303
|
].join("\n");
|
|
3304
|
+
case "template":
|
|
3305
|
+
return [
|
|
3306
|
+
"Usage: clickraft template <subcommand> [options]",
|
|
3307
|
+
"",
|
|
3308
|
+
"Subcommands:",
|
|
3309
|
+
" list List available templates",
|
|
3310
|
+
" get Fetch a template by ID",
|
|
3311
|
+
" find Search templates by keyword",
|
|
3312
|
+
""
|
|
3313
|
+
].join("\n");
|
|
3314
|
+
case "template list":
|
|
3315
|
+
return [
|
|
3316
|
+
"Usage: clickraft template list [options]",
|
|
3317
|
+
"",
|
|
3318
|
+
"Options:",
|
|
3319
|
+
" --scope <scope> Filter by scope: own, system, or all (default: all)",
|
|
3320
|
+
" --q <query> Free-text search query",
|
|
3321
|
+
" --tags <tag> Filter by tag (repeatable)",
|
|
3322
|
+
" --cursor <token> Pagination cursor from a previous response",
|
|
3323
|
+
" --limit <n> Max results per page (1-50, default: 20)",
|
|
3324
|
+
" --profile <name>",
|
|
3325
|
+
" --api-base-url <url>",
|
|
3326
|
+
" --token <token>",
|
|
3327
|
+
" --json",
|
|
3328
|
+
""
|
|
3329
|
+
].join("\n");
|
|
3330
|
+
case "template get":
|
|
3331
|
+
return [
|
|
3332
|
+
"Usage: clickraft template get <id> [options]",
|
|
3333
|
+
"",
|
|
3334
|
+
"Fetches a single template by its ID, including declared parameters.",
|
|
3335
|
+
"",
|
|
3336
|
+
"Options:",
|
|
3337
|
+
" --profile <name>",
|
|
3338
|
+
" --api-base-url <url>",
|
|
3339
|
+
" --token <token>",
|
|
3340
|
+
" --json",
|
|
3341
|
+
""
|
|
3342
|
+
].join("\n");
|
|
3343
|
+
case "template find":
|
|
3344
|
+
return [
|
|
3345
|
+
"Usage: clickraft template find <query> [options]",
|
|
3346
|
+
"",
|
|
3347
|
+
"Searches templates by keyword. Shorthand for `template list --q <query>`.",
|
|
3348
|
+
"",
|
|
3349
|
+
"Options:",
|
|
3350
|
+
" --tags <tag> Filter by tag (repeatable)",
|
|
3351
|
+
" --scope <scope> Filter by scope: own, system, or all (default: all)",
|
|
3352
|
+
" --limit <n> Max results per page (1-50, default: 20)",
|
|
3353
|
+
" --cursor <token> Pagination cursor from a previous response",
|
|
3354
|
+
" --profile <name>",
|
|
3355
|
+
" --api-base-url <url>",
|
|
3356
|
+
" --token <token>",
|
|
3357
|
+
" --json",
|
|
3358
|
+
""
|
|
3359
|
+
].join("\n");
|
|
3030
3360
|
case "generate":
|
|
3031
3361
|
return [
|
|
3032
3362
|
"Usage: clickraft generate <subcommand> [options]",
|
|
@@ -3047,7 +3377,9 @@ function renderCommandHelp(command) {
|
|
|
3047
3377
|
"Options:",
|
|
3048
3378
|
" --prompt <text> Text prompt (or pass as the positional argument)",
|
|
3049
3379
|
" --model-slug <slug> Required model identifier",
|
|
3050
|
-
" --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",
|
|
3051
3383
|
" --reference-image <v> URL or local path (repeatable, max 8)",
|
|
3052
3384
|
" --aspect-ratio <a:b> e.g. 16:9",
|
|
3053
3385
|
" --resolution <WxH> e.g. 1024x768",
|
|
@@ -3148,6 +3480,14 @@ function matchNewCommand(argv) {
|
|
|
3148
3480
|
if (sub === void 0 || sub.startsWith("-")) return { kind: "sub-help", verb: "jobs" };
|
|
3149
3481
|
return { kind: "unknown-sub", verb: "jobs", sub };
|
|
3150
3482
|
}
|
|
3483
|
+
if (verb === "template") {
|
|
3484
|
+
const sub = argv[1];
|
|
3485
|
+
if (sub === "list") return { kind: "cmd", path: "template.list", verbTokenCount: 2 };
|
|
3486
|
+
if (sub === "get") return { kind: "cmd", path: "template.get", verbTokenCount: 2 };
|
|
3487
|
+
if (sub === "find") return { kind: "cmd", path: "template.find", verbTokenCount: 2 };
|
|
3488
|
+
if (sub === void 0 || sub.startsWith("-")) return { kind: "sub-help", verb: "template" };
|
|
3489
|
+
return { kind: "unknown-sub", verb: "template", sub };
|
|
3490
|
+
}
|
|
3151
3491
|
return null;
|
|
3152
3492
|
}
|
|
3153
3493
|
async function runNewCommand(newCmd, options, stdout, stderr) {
|
|
@@ -3198,6 +3538,12 @@ async function runNewCommand(newCmd, options, stdout, stderr) {
|
|
|
3198
3538
|
return await runJobsWait(argvAfterVerb, ctx, rc);
|
|
3199
3539
|
case "upload":
|
|
3200
3540
|
return await runUploadCmd(argvAfterVerb, ctx, rc);
|
|
3541
|
+
case "template.list":
|
|
3542
|
+
return await runTemplateList(argvAfterVerb, ctx, rc);
|
|
3543
|
+
case "template.get":
|
|
3544
|
+
return await runTemplateGet(argvAfterVerb, ctx, rc);
|
|
3545
|
+
case "template.find":
|
|
3546
|
+
return await runTemplateFind(argvAfterVerb, ctx, rc);
|
|
3201
3547
|
}
|
|
3202
3548
|
} catch (err) {
|
|
3203
3549
|
if (isAbortedError(err, options.signal)) {
|
|
@@ -3228,7 +3574,6 @@ async function runGenerateCreate(argv, ctx, rc) {
|
|
|
3228
3574
|
...COMMON_RICH_STRING_FLAGS,
|
|
3229
3575
|
"prompt",
|
|
3230
3576
|
"model-slug",
|
|
3231
|
-
"brand-model",
|
|
3232
3577
|
"aspect-ratio",
|
|
3233
3578
|
"resolution",
|
|
3234
3579
|
"duration-seconds",
|
|
@@ -3237,10 +3582,13 @@ async function runGenerateCreate(argv, ctx, rc) {
|
|
|
3237
3582
|
"timeout"
|
|
3238
3583
|
],
|
|
3239
3584
|
boolean: [...COMMON_RICH_BOOL_FLAGS, "wait", "async"],
|
|
3240
|
-
array: ["reference-image"]
|
|
3585
|
+
array: ["reference-image", "brand-model"]
|
|
3241
3586
|
});
|
|
3242
3587
|
const modelSlug = parsed.string["model-slug"];
|
|
3243
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);
|
|
3244
3592
|
const result = await generateCreate({
|
|
3245
3593
|
prompt: parsed.string.prompt ?? parsed.positional[0],
|
|
3246
3594
|
modelSlug,
|
|
@@ -3248,7 +3596,7 @@ async function runGenerateCreate(argv, ctx, rc) {
|
|
|
3248
3596
|
aspectRatio: parsed.string["aspect-ratio"],
|
|
3249
3597
|
resolution: parsed.string.resolution,
|
|
3250
3598
|
durationSeconds: optionalInt(parsed.string["duration-seconds"], "duration-seconds"),
|
|
3251
|
-
|
|
3599
|
+
brandModels,
|
|
3252
3600
|
workflowId: parsed.string["workflow-id"],
|
|
3253
3601
|
nodeId: parsed.string["node-id"],
|
|
3254
3602
|
wait: computeWaitFlag(parsed),
|
|
@@ -3392,6 +3740,87 @@ function exitCodeForResult(result) {
|
|
|
3392
3740
|
}
|
|
3393
3741
|
return 0;
|
|
3394
3742
|
}
|
|
3743
|
+
async function runTemplateList(argv, ctx, rc) {
|
|
3744
|
+
const parsed = parseArgs(argv, {
|
|
3745
|
+
string: [...COMMON_RICH_STRING_FLAGS, "scope", "q", "cursor", "limit"],
|
|
3746
|
+
boolean: [...COMMON_RICH_BOOL_FLAGS],
|
|
3747
|
+
array: ["tags"]
|
|
3748
|
+
});
|
|
3749
|
+
const result = await listTemplates({
|
|
3750
|
+
q: parsed.string.q,
|
|
3751
|
+
tags: parsed.array.tags,
|
|
3752
|
+
scope: parsed.string.scope,
|
|
3753
|
+
cursor: parsed.string.cursor,
|
|
3754
|
+
limit: optionalInt(parsed.string.limit, "limit"),
|
|
3755
|
+
profile: parsed.string.profile,
|
|
3756
|
+
apiBaseUrl: parsed.string["api-base-url"],
|
|
3757
|
+
token: parsed.string.token,
|
|
3758
|
+
signal: rc.signal
|
|
3759
|
+
});
|
|
3760
|
+
ctx.nextCursor = result.cursor ?? void 0;
|
|
3761
|
+
emitTemplateList(result, ctx, rc);
|
|
3762
|
+
return 0;
|
|
3763
|
+
}
|
|
3764
|
+
async function runTemplateGet(argv, ctx, rc) {
|
|
3765
|
+
const parsed = parseArgs(argv, {
|
|
3766
|
+
string: [...COMMON_RICH_STRING_FLAGS],
|
|
3767
|
+
boolean: [...COMMON_RICH_BOOL_FLAGS]
|
|
3768
|
+
});
|
|
3769
|
+
const id = parsed.positional[0];
|
|
3770
|
+
if (!id) throw new ArgError("Missing required <id> argument.");
|
|
3771
|
+
const detail = await getTemplate({
|
|
3772
|
+
id,
|
|
3773
|
+
profile: parsed.string.profile,
|
|
3774
|
+
apiBaseUrl: parsed.string["api-base-url"],
|
|
3775
|
+
token: parsed.string.token,
|
|
3776
|
+
signal: rc.signal
|
|
3777
|
+
});
|
|
3778
|
+
emitTemplateDetail(detail, ctx, rc);
|
|
3779
|
+
return 0;
|
|
3780
|
+
}
|
|
3781
|
+
async function runTemplateFind(argv, ctx, rc) {
|
|
3782
|
+
const parsed = parseArgs(argv, {
|
|
3783
|
+
string: [...COMMON_RICH_STRING_FLAGS, "scope", "cursor", "limit"],
|
|
3784
|
+
boolean: [...COMMON_RICH_BOOL_FLAGS],
|
|
3785
|
+
array: ["tags"]
|
|
3786
|
+
});
|
|
3787
|
+
const query = parsed.positional[0];
|
|
3788
|
+
if (!query) throw new ArgError("Missing required <query> argument.");
|
|
3789
|
+
const result = await findTemplates({
|
|
3790
|
+
query,
|
|
3791
|
+
tags: parsed.array.tags,
|
|
3792
|
+
scope: parsed.string.scope,
|
|
3793
|
+
cursor: parsed.string.cursor,
|
|
3794
|
+
limit: optionalInt(parsed.string.limit, "limit"),
|
|
3795
|
+
profile: parsed.string.profile,
|
|
3796
|
+
apiBaseUrl: parsed.string["api-base-url"],
|
|
3797
|
+
token: parsed.string.token,
|
|
3798
|
+
signal: rc.signal
|
|
3799
|
+
});
|
|
3800
|
+
ctx.nextCursor = result.cursor ?? void 0;
|
|
3801
|
+
emitTemplateList(result, ctx, rc);
|
|
3802
|
+
return 0;
|
|
3803
|
+
}
|
|
3804
|
+
function emitTemplateList(result, ctx, rc) {
|
|
3805
|
+
if (rc.signal?.aborted) {
|
|
3806
|
+
throw new AuthError("AUTH_FLOW_CANCELLED", "Cancelled.");
|
|
3807
|
+
}
|
|
3808
|
+
if (rc.jsonMode) {
|
|
3809
|
+
rc.stdout.write(JSON.stringify(wrapSuccess(result, ctx)) + "\n");
|
|
3810
|
+
return;
|
|
3811
|
+
}
|
|
3812
|
+
renderTemplatesList(result, rc.stdout);
|
|
3813
|
+
}
|
|
3814
|
+
function emitTemplateDetail(detail, ctx, rc) {
|
|
3815
|
+
if (rc.signal?.aborted) {
|
|
3816
|
+
throw new AuthError("AUTH_FLOW_CANCELLED", "Cancelled.");
|
|
3817
|
+
}
|
|
3818
|
+
if (rc.jsonMode) {
|
|
3819
|
+
rc.stdout.write(JSON.stringify(wrapSuccess(detail, ctx)) + "\n");
|
|
3820
|
+
return;
|
|
3821
|
+
}
|
|
3822
|
+
renderTemplateDetail(detail, rc.stdout);
|
|
3823
|
+
}
|
|
3395
3824
|
|
|
3396
3825
|
// src/index.ts
|
|
3397
3826
|
var __filename2 = fileURLToPath(import.meta.url);
|