@clickraft/cli 0.4.0 → 0.5.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 CHANGED
@@ -1,9 +1,8 @@
1
- ## [0.4.0](https://github.com/clickraft/cli/compare/v0.3.0...v0.4.0) (2026-05-24)
1
+ ## [0.5.0](https://github.com/clickraft/cli/compare/v0.4.0...v0.5.0) (2026-05-24)
2
2
 
3
3
  ### Features
4
4
 
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))
5
+ * **models:** add models list command consuming agents/v1/models ([83761d1](https://github.com/clickraft/cli/commit/83761d11d136758e23c40cf80cd87c8f08dbaad6))
7
6
 
8
7
  # Changelog
9
8
 
package/README.md CHANGED
@@ -19,6 +19,8 @@ clickraft login
19
19
  clickraft generate create --prompt "a serene mountain landscape at dawn"
20
20
  clickraft balance
21
21
  clickraft brand-model list
22
+ clickraft models list
23
+ clickraft models list --category image --tier pro
22
24
  clickraft template list
23
25
  clickraft template get <template-id>
24
26
  clickraft template find "coffee brand"
@@ -56,6 +58,7 @@ export CLOUDFLARE_ACCESS_CLIENT_SECRET="<client-secret>"
56
58
  clickraft tokens list --profile staging
57
59
  clickraft balance --profile staging
58
60
  clickraft brand-model list --profile staging
61
+ clickraft models list --profile staging
59
62
  ```
60
63
 
61
64
  Both env vars must be set together — if only one is set the CLI exits with a configuration error. Production usage (the default) does not require these env vars.
package/dist/cli.js CHANGED
@@ -2441,6 +2441,77 @@ function formatRevokeFailure(err, token) {
2441
2441
  return `warning: server-side revoke failed (${reason}). Local profile removed; token ${redactTokenLikeValue(token)} remains server-valid until expiry.`;
2442
2442
  }
2443
2443
 
2444
+ // src/schemas/models.ts
2445
+ import { z as z8 } from "zod";
2446
+ var ModelSummarySchema = z8.object({
2447
+ id: z8.string().uuid(),
2448
+ slug: z8.string(),
2449
+ displayName: z8.string(),
2450
+ description: z8.string().nullable(),
2451
+ shortDescription: z8.string().nullable(),
2452
+ providerSlug: z8.string(),
2453
+ providerName: z8.string(),
2454
+ modelType: z8.string(),
2455
+ category: z8.string(),
2456
+ costTier: z8.string(),
2457
+ creditCost: z8.number(),
2458
+ pricingStrategy: z8.string(),
2459
+ estimatedSeconds: z8.object({
2460
+ typical: z8.number(),
2461
+ range: z8.array(z8.number())
2462
+ }).passthrough().nullable(),
2463
+ status: z8.string(),
2464
+ modalities: z8.object({
2465
+ input: z8.array(z8.string()),
2466
+ output: z8.array(z8.string())
2467
+ }).passthrough().nullable(),
2468
+ bestFor: z8.array(z8.string()),
2469
+ badges: z8.array(z8.string()),
2470
+ isFeatured: z8.boolean(),
2471
+ isDefault: z8.boolean(),
2472
+ docsUrl: z8.string().nullable(),
2473
+ llmContext: z8.string().nullable(),
2474
+ constraints: z8.record(z8.unknown()).nullable(),
2475
+ modelFamily: z8.string().nullable(),
2476
+ isFamilyPrimary: z8.boolean().nullable()
2477
+ }).strict();
2478
+ var ModelsListResponseSchema = z8.object({
2479
+ models: z8.array(ModelSummarySchema)
2480
+ }).strict();
2481
+
2482
+ // src/commands/models/internal.ts
2483
+ async function buildModelClient(opts) {
2484
+ const cfg = await loadConfig({
2485
+ flagProfile: opts.profile,
2486
+ flagApiBaseUrl: opts.apiBaseUrl,
2487
+ flagToken: opts.token
2488
+ });
2489
+ return new ApiClient({
2490
+ apiBaseUrl: cfg.apiBaseUrl,
2491
+ accessToken: cfg.accessToken,
2492
+ signal: opts.signal,
2493
+ cloudflareAccess: cfg.cloudflareAccess
2494
+ });
2495
+ }
2496
+
2497
+ // src/commands/models/list.ts
2498
+ async function listModels(opts) {
2499
+ const client = opts.client ?? await buildModelClient(opts);
2500
+ const data = await client.request({
2501
+ method: "GET",
2502
+ path: "/models",
2503
+ query: {
2504
+ category: opts.category,
2505
+ tier: opts.tier,
2506
+ type: opts.type,
2507
+ featured: opts.featured !== void 0 ? String(opts.featured) : void 0
2508
+ },
2509
+ responseSchema: ModelsListResponseSchema,
2510
+ signal: opts.signal
2511
+ });
2512
+ return data;
2513
+ }
2514
+
2444
2515
  // src/telemetry/detect.ts
2445
2516
  var AGENT_SIGNALS = [
2446
2517
  { envVar: "CLAUDE_CODE_SESSION_ID", agent: "claude-code" },
@@ -2531,33 +2602,33 @@ async function buildTemplateClient(opts) {
2531
2602
  }
2532
2603
 
2533
2604
  // src/schemas/templates.ts
2534
- import { z as z8 } from "zod";
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()
2605
+ import { z as z9 } from "zod";
2606
+ var DeclaredParamSchema = z9.object({
2607
+ name: z9.string(),
2608
+ type: z9.string(),
2609
+ required: z9.boolean(),
2610
+ defaultValue: z9.string().nullable().optional(),
2611
+ enumValues: z9.array(z9.string()).nullable().optional(),
2612
+ description: z9.string().nullable().optional()
2542
2613
  }).strict();
2543
- var TemplateSummarySchema = z8.object({
2544
- id: z8.string().uuid(),
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()),
2551
- createdAt: z8.string()
2614
+ var TemplateSummarySchema = z9.object({
2615
+ id: z9.string().uuid(),
2616
+ source: z9.enum(["own", "system"]),
2617
+ name: z9.string(),
2618
+ description: z9.string().nullable(),
2619
+ category: z9.string().nullable(),
2620
+ thumbnailUrl: z9.string().nullable(),
2621
+ tags: z9.array(z9.string()),
2622
+ createdAt: z9.string()
2552
2623
  }).strict();
2553
2624
  var TemplateDetailSchema = TemplateSummarySchema.extend({
2554
- declaredParams: z8.array(DeclaredParamSchema),
2555
- timesUsed: z8.number()
2625
+ declaredParams: z9.array(DeclaredParamSchema),
2626
+ timesUsed: z9.number()
2556
2627
  }).strict();
2557
- var TemplatesListResponseSchema = z8.object({
2558
- templates: z8.array(TemplateSummarySchema),
2559
- cursor: z8.string().nullable(),
2560
- hasMore: z8.boolean()
2628
+ var TemplatesListResponseSchema = z9.object({
2629
+ templates: z9.array(TemplateSummarySchema),
2630
+ cursor: z9.string().nullable(),
2631
+ hasMore: z9.boolean()
2561
2632
  }).strict();
2562
2633
 
2563
2634
  // src/commands/template/list.ts
@@ -2607,20 +2678,20 @@ async function getTemplate(opts) {
2607
2678
  }
2608
2679
 
2609
2680
  // 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()
2681
+ import { z as z10 } from "zod";
2682
+ var TokenRowSchema = z10.object({
2683
+ id: z10.string().uuid(),
2684
+ organizationId: z10.string().uuid(),
2685
+ userId: z10.string().uuid(),
2686
+ clientId: z10.string().nullable(),
2687
+ tokenPrefix: z10.string(),
2688
+ name: z10.string().nullable(),
2689
+ scopes: z10.array(z10.string()),
2690
+ lastUsedAt: z10.string().nullable(),
2691
+ expiresAt: z10.string(),
2692
+ createdAt: z10.string()
2622
2693
  }).strict();
2623
- var TokensListResponseSchema = z9.array(TokenRowSchema);
2694
+ var TokensListResponseSchema = z10.array(TokenRowSchema);
2624
2695
  async function listTokens(options) {
2625
2696
  const config = await loadConfig({
2626
2697
  flagProfile: options.profile,
@@ -2988,6 +3059,72 @@ function renderTemplateDetail(detail, out) {
2988
3059
  }
2989
3060
  out.write(lines.join("\n") + "\n");
2990
3061
  }
3062
+ var TIER_ORDER = { premium: 0, pro: 1, standard: 2 };
3063
+ function renderModelsList(result, out, options) {
3064
+ const { models } = result;
3065
+ if (models.length === 0) {
3066
+ out.write("No models match your filters.\n");
3067
+ return;
3068
+ }
3069
+ const sorted = [...models].sort((a, b) => {
3070
+ const catCmp = a.category.localeCompare(b.category);
3071
+ if (catCmp !== 0) return catCmp;
3072
+ const tierA = TIER_ORDER[a.costTier] ?? 99;
3073
+ const tierB = TIER_ORDER[b.costTier] ?? 99;
3074
+ if (tierA !== tierB) return tierA - tierB;
3075
+ return a.slug.localeCompare(b.slug);
3076
+ });
3077
+ const filterParts = [];
3078
+ if (options?.filters) {
3079
+ for (const [k, v] of Object.entries(options.filters)) {
3080
+ if (v) filterParts.push(`${k}=${v}`);
3081
+ }
3082
+ }
3083
+ const filterSuffix = filterParts.length > 0 ? ` (filters: ${filterParts.join(", ")})` : "";
3084
+ out.write(`${sorted.length} models${filterSuffix}
3085
+
3086
+ `);
3087
+ const headers = ["SLUG", "NAME", "CATEGORY", "TIER", "CREDITS", "DEFAULT"];
3088
+ const data = sorted.map((m) => [
3089
+ m.slug,
3090
+ m.displayName,
3091
+ m.category,
3092
+ m.costTier,
3093
+ String(m.creditCost),
3094
+ m.isDefault ? "\u2713" : ""
3095
+ ]);
3096
+ const widths = headers.map(
3097
+ (header, i) => Math.max(header.length, ...data.map((cells) => cells[i].length))
3098
+ );
3099
+ const padRow = (cells) => cells.map((cell, i) => cell.padEnd(widths[i])).join(" ").trimEnd();
3100
+ out.write(padRow(headers) + "\n");
3101
+ for (const cells of data) out.write(padRow(cells) + "\n");
3102
+ out.write(
3103
+ `
3104
+ Showing all ${sorted.length} models. Use --json for full details including constraints.
3105
+ `
3106
+ );
3107
+ if (options?.verbose) {
3108
+ out.write("\n");
3109
+ for (const m of sorted) {
3110
+ const pad = 16;
3111
+ out.write(`${m.slug} (${m.displayName})
3112
+ `);
3113
+ out.write(`${" Provider:".padEnd(pad)}${m.providerName}
3114
+ `);
3115
+ out.write(`${" Category:".padEnd(pad)}${m.category} / ${m.modelType}
3116
+ `);
3117
+ out.write(`${" Tier:".padEnd(pad)}${m.costTier} (${m.creditCost} credits)
3118
+ `);
3119
+ if (m.llmContext) {
3120
+ const ctx = m.llmContext.length > 100 ? m.llmContext.slice(0, 100) + "..." : m.llmContext;
3121
+ out.write(`${" Context:".padEnd(pad)}${ctx}
3122
+ `);
3123
+ }
3124
+ out.write("\n");
3125
+ }
3126
+ }
3127
+ }
2991
3128
  function formatExpiresAt(iso) {
2992
3129
  const ms = Date.parse(iso);
2993
3130
  if (Number.isNaN(ms)) return iso;
@@ -3214,6 +3351,7 @@ function renderRootHelp(version) {
3214
3351
  " tokens list List the caller's API tokens",
3215
3352
  " balance Show credit balance and rate limits",
3216
3353
  " brand-model list List available brand models",
3354
+ " models list List available AI models",
3217
3355
  " telemetry inspect Show current telemetry state and payload shape",
3218
3356
  " template list List available templates",
3219
3357
  " template get Fetch a template by ID",
@@ -3293,6 +3431,38 @@ function renderCommandHelp(command) {
3293
3431
  " --json Emit JSON envelope instead of human output",
3294
3432
  ""
3295
3433
  ].join("\n");
3434
+ case "models":
3435
+ return [
3436
+ "Usage: clickraft models <subcommand> [options]",
3437
+ "",
3438
+ "Subcommands:",
3439
+ " list List available AI models",
3440
+ ""
3441
+ ].join("\n");
3442
+ case "models list":
3443
+ return [
3444
+ "Usage: clickraft models list [options]",
3445
+ "",
3446
+ "List AI models available for generation. By default shows a summary table;",
3447
+ "use --json for full details including constraints and llmContext.",
3448
+ "",
3449
+ "Options:",
3450
+ " --category <name> Filter by category (image, video, audio, chat, edit)",
3451
+ " --tier <name> Filter by cost tier (standard, pro, premium)",
3452
+ " --type <name> Filter by model type (e.g. text-to-image)",
3453
+ " --featured Show only featured models",
3454
+ " --verbose Show expanded per-model details after the table",
3455
+ " --profile <name>",
3456
+ " --api-base-url <url>",
3457
+ " --token <token>",
3458
+ " --json",
3459
+ "",
3460
+ "Examples:",
3461
+ " clickraft models list",
3462
+ " clickraft models list --category image --tier pro",
3463
+ " clickraft models list --featured --json",
3464
+ ""
3465
+ ].join("\n");
3296
3466
  case "telemetry inspect":
3297
3467
  return [
3298
3468
  "Usage: clickraft telemetry inspect [options]",
@@ -3488,6 +3658,12 @@ function matchNewCommand(argv) {
3488
3658
  if (sub === void 0 || sub.startsWith("-")) return { kind: "sub-help", verb: "template" };
3489
3659
  return { kind: "unknown-sub", verb: "template", sub };
3490
3660
  }
3661
+ if (verb === "models") {
3662
+ const sub = argv[1];
3663
+ if (sub === "list") return { kind: "cmd", path: "models.list", verbTokenCount: 2 };
3664
+ if (sub === void 0 || sub.startsWith("-")) return { kind: "sub-help", verb: "models" };
3665
+ return { kind: "unknown-sub", verb: "models", sub };
3666
+ }
3491
3667
  return null;
3492
3668
  }
3493
3669
  async function runNewCommand(newCmd, options, stdout, stderr) {
@@ -3544,6 +3720,8 @@ async function runNewCommand(newCmd, options, stdout, stderr) {
3544
3720
  return await runTemplateGet(argvAfterVerb, ctx, rc);
3545
3721
  case "template.find":
3546
3722
  return await runTemplateFind(argvAfterVerb, ctx, rc);
3723
+ case "models.list":
3724
+ return await runModelsList(argvAfterVerb, ctx, rc);
3547
3725
  }
3548
3726
  } catch (err) {
3549
3727
  if (isAbortedError(err, options.signal)) {
@@ -3821,6 +3999,42 @@ function emitTemplateDetail(detail, ctx, rc) {
3821
3999
  }
3822
4000
  renderTemplateDetail(detail, rc.stdout);
3823
4001
  }
4002
+ async function runModelsList(argv, ctx, rc) {
4003
+ const parsed = parseArgs(argv, {
4004
+ string: [...COMMON_RICH_STRING_FLAGS, "category", "tier", "type"],
4005
+ boolean: [...COMMON_RICH_BOOL_FLAGS, "featured", "verbose"]
4006
+ });
4007
+ const result = await listModels({
4008
+ category: parsed.string.category,
4009
+ tier: parsed.string.tier,
4010
+ type: parsed.string.type,
4011
+ featured: parsed.boolean.featured,
4012
+ profile: parsed.string.profile,
4013
+ apiBaseUrl: parsed.string["api-base-url"],
4014
+ token: parsed.string.token,
4015
+ signal: rc.signal
4016
+ });
4017
+ emitModelsList(result, ctx, rc, parsed);
4018
+ return 0;
4019
+ }
4020
+ function emitModelsList(result, ctx, rc, parsed) {
4021
+ if (rc.signal?.aborted) {
4022
+ throw new AuthError("AUTH_FLOW_CANCELLED", "Cancelled.");
4023
+ }
4024
+ if (rc.jsonMode) {
4025
+ rc.stdout.write(JSON.stringify(wrapSuccess(result, ctx)) + "\n");
4026
+ return;
4027
+ }
4028
+ renderModelsList(result, rc.stdout, {
4029
+ verbose: parsed.boolean.verbose === true,
4030
+ filters: {
4031
+ category: parsed.string.category ?? "",
4032
+ tier: parsed.string.tier ?? "",
4033
+ type: parsed.string.type ?? "",
4034
+ featured: parsed.boolean.featured !== void 0 ? String(parsed.boolean.featured) : ""
4035
+ }
4036
+ });
4037
+ }
3824
4038
 
3825
4039
  // src/index.ts
3826
4040
  var __filename2 = fileURLToPath(import.meta.url);