@clickraft/cli 0.2.0 → 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.
package/dist/index.js CHANGED
@@ -721,6 +721,10 @@ function buildUrl(baseUrl, path, query) {
721
721
  if (query) {
722
722
  for (const [key, value] of Object.entries(query)) {
723
723
  if (value === void 0) continue;
724
+ if (Array.isArray(value)) {
725
+ for (const item of value) url.searchParams.append(key, item);
726
+ continue;
727
+ }
724
728
  url.searchParams.append(key, String(value));
725
729
  }
726
730
  }
@@ -2305,21 +2309,112 @@ function inspectTelemetry(options = {}) {
2305
2309
  };
2306
2310
  }
2307
2311
 
2308
- // src/commands/tokens/list.ts
2312
+ // src/commands/template/internal.ts
2313
+ async function buildTemplateClient(opts) {
2314
+ const cfg = await loadConfig({
2315
+ flagProfile: opts.profile,
2316
+ flagApiBaseUrl: opts.apiBaseUrl,
2317
+ flagToken: opts.token
2318
+ });
2319
+ return new ApiClient({
2320
+ apiBaseUrl: cfg.apiBaseUrl,
2321
+ accessToken: cfg.accessToken,
2322
+ signal: opts.signal,
2323
+ cloudflareAccess: cfg.cloudflareAccess
2324
+ });
2325
+ }
2326
+
2327
+ // src/schemas/templates.ts
2309
2328
  import { z as z8 } from "zod";
2310
- var TokenRowSchema = z8.object({
2329
+ var DeclaredParamSchema = z8.object({
2330
+ name: z8.string(),
2331
+ type: z8.string(),
2332
+ required: z8.boolean(),
2333
+ defaultValue: z8.string().nullable().optional(),
2334
+ enumValues: z8.array(z8.string()).nullable().optional(),
2335
+ description: z8.string().nullable().optional()
2336
+ }).strict();
2337
+ var TemplateSummarySchema = z8.object({
2311
2338
  id: z8.string().uuid(),
2312
- organizationId: z8.string().uuid(),
2313
- userId: z8.string().uuid(),
2314
- clientId: z8.string().nullable(),
2315
- tokenPrefix: z8.string(),
2316
- name: z8.string().nullable(),
2317
- scopes: z8.array(z8.string()),
2318
- lastUsedAt: z8.string().nullable(),
2319
- expiresAt: z8.string(),
2339
+ source: z8.enum(["own", "system"]),
2340
+ name: z8.string(),
2341
+ description: z8.string().nullable(),
2342
+ category: z8.string().nullable(),
2343
+ thumbnailUrl: z8.string().nullable(),
2344
+ tags: z8.array(z8.string()),
2320
2345
  createdAt: z8.string()
2321
2346
  }).strict();
2322
- var TokensListResponseSchema = z8.array(TokenRowSchema);
2347
+ var TemplateDetailSchema = TemplateSummarySchema.extend({
2348
+ declaredParams: z8.array(DeclaredParamSchema),
2349
+ timesUsed: z8.number()
2350
+ }).strict();
2351
+ var TemplatesListResponseSchema = z8.object({
2352
+ templates: z8.array(TemplateSummarySchema),
2353
+ cursor: z8.string().nullable(),
2354
+ hasMore: z8.boolean()
2355
+ }).strict();
2356
+
2357
+ // src/commands/template/list.ts
2358
+ async function listTemplates(opts) {
2359
+ const client = opts.client ?? await buildTemplateClient(opts);
2360
+ const data = await client.request({
2361
+ method: "GET",
2362
+ path: "/templates",
2363
+ query: {
2364
+ q: opts.q,
2365
+ tags: opts.tags,
2366
+ scope: opts.scope,
2367
+ cursor: opts.cursor,
2368
+ limit: opts.limit
2369
+ },
2370
+ responseSchema: TemplatesListResponseSchema,
2371
+ signal: opts.signal
2372
+ });
2373
+ return data;
2374
+ }
2375
+
2376
+ // src/commands/template/find.ts
2377
+ async function findTemplates(opts) {
2378
+ return listTemplates({
2379
+ q: opts.query,
2380
+ tags: opts.tags,
2381
+ scope: opts.scope,
2382
+ limit: opts.limit,
2383
+ cursor: opts.cursor,
2384
+ profile: opts.profile,
2385
+ apiBaseUrl: opts.apiBaseUrl,
2386
+ token: opts.token,
2387
+ signal: opts.signal,
2388
+ client: opts.client
2389
+ });
2390
+ }
2391
+
2392
+ // src/commands/template/get.ts
2393
+ async function getTemplate(opts) {
2394
+ const client = opts.client ?? await buildTemplateClient(opts);
2395
+ return client.request({
2396
+ method: "GET",
2397
+ path: `/templates/${encodeURIComponent(opts.id)}`,
2398
+ responseSchema: TemplateDetailSchema,
2399
+ signal: opts.signal
2400
+ });
2401
+ }
2402
+
2403
+ // src/commands/tokens/list.ts
2404
+ import { z as z9 } from "zod";
2405
+ var TokenRowSchema = z9.object({
2406
+ id: z9.string().uuid(),
2407
+ organizationId: z9.string().uuid(),
2408
+ userId: z9.string().uuid(),
2409
+ clientId: z9.string().nullable(),
2410
+ tokenPrefix: z9.string(),
2411
+ name: z9.string().nullable(),
2412
+ scopes: z9.array(z9.string()),
2413
+ lastUsedAt: z9.string().nullable(),
2414
+ expiresAt: z9.string(),
2415
+ createdAt: z9.string()
2416
+ }).strict();
2417
+ var TokensListResponseSchema = z9.array(TokenRowSchema);
2323
2418
  async function listTokens(options) {
2324
2419
  const config = await loadConfig({
2325
2420
  flagProfile: options.profile,
@@ -2717,6 +2812,61 @@ function renderBrandModelsList(models, out) {
2717
2812
  out.write(padRow(headers) + "\n");
2718
2813
  for (const cells of data) out.write(padRow(cells) + "\n");
2719
2814
  }
2815
+ function renderTemplatesList(result, out) {
2816
+ const { templates } = result;
2817
+ if (templates.length === 0) {
2818
+ out.write("No templates.\n");
2819
+ return;
2820
+ }
2821
+ const MAX_TAGS = 3;
2822
+ const headers = ["SOURCE", "NAME", "CATEGORY", "TAGS", "CREATED"];
2823
+ const data = templates.map((t) => {
2824
+ const shown = t.tags.slice(0, MAX_TAGS).join(", ");
2825
+ const extra = t.tags.length > MAX_TAGS ? ` +${t.tags.length - MAX_TAGS} more` : "";
2826
+ return [
2827
+ t.source,
2828
+ t.name,
2829
+ t.category ?? "\u2014",
2830
+ t.tags.length > 0 ? shown + extra : "\u2014",
2831
+ t.createdAt.slice(0, 10)
2832
+ ];
2833
+ });
2834
+ const widths = headers.map(
2835
+ (header, i) => Math.max(header.length, ...data.map((cells) => cells[i].length))
2836
+ );
2837
+ const padRow = (cells) => cells.map((cell, i) => cell.padEnd(widths[i])).join(" ").trimEnd();
2838
+ out.write(padRow(headers) + "\n");
2839
+ for (const cells of data) out.write(padRow(cells) + "\n");
2840
+ if (result.hasMore) {
2841
+ out.write("\nMore results available. Use --cursor to fetch the next page.\n");
2842
+ }
2843
+ }
2844
+ function renderTemplateDetail(detail, out) {
2845
+ const pad = 17;
2846
+ const lines = [
2847
+ detail.name,
2848
+ `${"ID:".padEnd(pad)}${detail.id}`,
2849
+ `${"Source:".padEnd(pad)}${detail.source}`,
2850
+ `${"Category:".padEnd(pad)}${detail.category ?? "\u2014"}`,
2851
+ `${"Tags:".padEnd(pad)}${detail.tags.length > 0 ? detail.tags.join(", ") : "\u2014"}`,
2852
+ `${"Created:".padEnd(pad)}${detail.createdAt.slice(0, 10)}`,
2853
+ `${"Times used:".padEnd(pad)}${detail.timesUsed}`
2854
+ ];
2855
+ if (detail.description) {
2856
+ lines.push("", detail.description);
2857
+ }
2858
+ lines.push("", "Declared parameters:");
2859
+ if (detail.declaredParams.length === 0) {
2860
+ lines.push(" (none)");
2861
+ } else {
2862
+ for (const p of detail.declaredParams) {
2863
+ const req = p.required ? "required" : "optional";
2864
+ const desc = p.description ? `: ${p.description}` : "";
2865
+ lines.push(` - ${p.name} (${p.type}, ${req})${desc}`);
2866
+ }
2867
+ }
2868
+ out.write(lines.join("\n") + "\n");
2869
+ }
2720
2870
  function formatExpiresAt(iso) {
2721
2871
  const ms = Date.parse(iso);
2722
2872
  if (Number.isNaN(ms)) return iso;
@@ -2944,6 +3094,9 @@ function renderRootHelp(version) {
2944
3094
  " balance Show credit balance and rate limits",
2945
3095
  " brand-model list List available brand models",
2946
3096
  " telemetry inspect Show current telemetry state and payload shape",
3097
+ " template list List available templates",
3098
+ " template get Fetch a template by ID",
3099
+ " template find Search templates by keyword",
2947
3100
  " generate create Submit a generation job and wait for completion",
2948
3101
  " generate wait Wait for an existing job to reach a terminal state",
2949
3102
  " generate get Fetch the current state of a job",
@@ -3027,6 +3180,62 @@ function renderCommandHelp(command) {
3027
3180
  " --json Emit JSON envelope instead of human output",
3028
3181
  ""
3029
3182
  ].join("\n");
3183
+ case "template":
3184
+ return [
3185
+ "Usage: clickraft template <subcommand> [options]",
3186
+ "",
3187
+ "Subcommands:",
3188
+ " list List available templates",
3189
+ " get Fetch a template by ID",
3190
+ " find Search templates by keyword",
3191
+ ""
3192
+ ].join("\n");
3193
+ case "template list":
3194
+ return [
3195
+ "Usage: clickraft template list [options]",
3196
+ "",
3197
+ "Options:",
3198
+ " --scope <scope> Filter by scope: own, system, or all (default: all)",
3199
+ " --q <query> Free-text search query",
3200
+ " --tags <tag> Filter by tag (repeatable)",
3201
+ " --cursor <token> Pagination cursor from a previous response",
3202
+ " --limit <n> Max results per page (1-50, default: 20)",
3203
+ " --profile <name>",
3204
+ " --api-base-url <url>",
3205
+ " --token <token>",
3206
+ " --json",
3207
+ ""
3208
+ ].join("\n");
3209
+ case "template get":
3210
+ return [
3211
+ "Usage: clickraft template get <id> [options]",
3212
+ "",
3213
+ "Fetches a single template by its ID, including declared parameters.",
3214
+ "",
3215
+ "Options:",
3216
+ " --profile <name>",
3217
+ " --api-base-url <url>",
3218
+ " --token <token>",
3219
+ " --json",
3220
+ ""
3221
+ ].join("\n");
3222
+ case "template find":
3223
+ return [
3224
+ "Usage: clickraft template find <query> [options]",
3225
+ "",
3226
+ "Searches templates by keyword. Shorthand for `template list --q <query>`.",
3227
+ "",
3228
+ "Options:",
3229
+ " --tags <tag> Filter by tag (repeatable)",
3230
+ " --scope <scope> Filter by scope: own, system, or all (default: all)",
3231
+ " --limit <n> Max results per page (1-50, default: 20)",
3232
+ " --cursor <token> Pagination cursor from a previous response",
3233
+ " --profile <name>",
3234
+ " --api-base-url <url>",
3235
+ " --token <token>",
3236
+ " --json",
3237
+ ""
3238
+ ].join("\n");
3030
3239
  case "generate":
3031
3240
  return [
3032
3241
  "Usage: clickraft generate <subcommand> [options]",
@@ -3148,6 +3357,14 @@ function matchNewCommand(argv) {
3148
3357
  if (sub === void 0 || sub.startsWith("-")) return { kind: "sub-help", verb: "jobs" };
3149
3358
  return { kind: "unknown-sub", verb: "jobs", sub };
3150
3359
  }
3360
+ if (verb === "template") {
3361
+ const sub = argv[1];
3362
+ if (sub === "list") return { kind: "cmd", path: "template.list", verbTokenCount: 2 };
3363
+ if (sub === "get") return { kind: "cmd", path: "template.get", verbTokenCount: 2 };
3364
+ if (sub === "find") return { kind: "cmd", path: "template.find", verbTokenCount: 2 };
3365
+ if (sub === void 0 || sub.startsWith("-")) return { kind: "sub-help", verb: "template" };
3366
+ return { kind: "unknown-sub", verb: "template", sub };
3367
+ }
3151
3368
  return null;
3152
3369
  }
3153
3370
  async function runNewCommand(newCmd, options, stdout, stderr) {
@@ -3198,6 +3415,12 @@ async function runNewCommand(newCmd, options, stdout, stderr) {
3198
3415
  return await runJobsWait(argvAfterVerb, ctx, rc);
3199
3416
  case "upload":
3200
3417
  return await runUploadCmd(argvAfterVerb, ctx, rc);
3418
+ case "template.list":
3419
+ return await runTemplateList(argvAfterVerb, ctx, rc);
3420
+ case "template.get":
3421
+ return await runTemplateGet(argvAfterVerb, ctx, rc);
3422
+ case "template.find":
3423
+ return await runTemplateFind(argvAfterVerb, ctx, rc);
3201
3424
  }
3202
3425
  } catch (err) {
3203
3426
  if (isAbortedError(err, options.signal)) {
@@ -3392,6 +3615,87 @@ function exitCodeForResult(result) {
3392
3615
  }
3393
3616
  return 0;
3394
3617
  }
3618
+ async function runTemplateList(argv, ctx, rc) {
3619
+ const parsed = parseArgs(argv, {
3620
+ string: [...COMMON_RICH_STRING_FLAGS, "scope", "q", "cursor", "limit"],
3621
+ boolean: [...COMMON_RICH_BOOL_FLAGS],
3622
+ array: ["tags"]
3623
+ });
3624
+ const result = await listTemplates({
3625
+ q: parsed.string.q,
3626
+ tags: parsed.array.tags,
3627
+ scope: parsed.string.scope,
3628
+ cursor: parsed.string.cursor,
3629
+ limit: optionalInt(parsed.string.limit, "limit"),
3630
+ profile: parsed.string.profile,
3631
+ apiBaseUrl: parsed.string["api-base-url"],
3632
+ token: parsed.string.token,
3633
+ signal: rc.signal
3634
+ });
3635
+ ctx.nextCursor = result.cursor ?? void 0;
3636
+ emitTemplateList(result, ctx, rc);
3637
+ return 0;
3638
+ }
3639
+ async function runTemplateGet(argv, ctx, rc) {
3640
+ const parsed = parseArgs(argv, {
3641
+ string: [...COMMON_RICH_STRING_FLAGS],
3642
+ boolean: [...COMMON_RICH_BOOL_FLAGS]
3643
+ });
3644
+ const id = parsed.positional[0];
3645
+ if (!id) throw new ArgError("Missing required <id> argument.");
3646
+ const detail = await getTemplate({
3647
+ id,
3648
+ profile: parsed.string.profile,
3649
+ apiBaseUrl: parsed.string["api-base-url"],
3650
+ token: parsed.string.token,
3651
+ signal: rc.signal
3652
+ });
3653
+ emitTemplateDetail(detail, ctx, rc);
3654
+ return 0;
3655
+ }
3656
+ async function runTemplateFind(argv, ctx, rc) {
3657
+ const parsed = parseArgs(argv, {
3658
+ string: [...COMMON_RICH_STRING_FLAGS, "scope", "cursor", "limit"],
3659
+ boolean: [...COMMON_RICH_BOOL_FLAGS],
3660
+ array: ["tags"]
3661
+ });
3662
+ const query = parsed.positional[0];
3663
+ if (!query) throw new ArgError("Missing required <query> argument.");
3664
+ const result = await findTemplates({
3665
+ query,
3666
+ tags: parsed.array.tags,
3667
+ scope: parsed.string.scope,
3668
+ cursor: parsed.string.cursor,
3669
+ limit: optionalInt(parsed.string.limit, "limit"),
3670
+ profile: parsed.string.profile,
3671
+ apiBaseUrl: parsed.string["api-base-url"],
3672
+ token: parsed.string.token,
3673
+ signal: rc.signal
3674
+ });
3675
+ ctx.nextCursor = result.cursor ?? void 0;
3676
+ emitTemplateList(result, ctx, rc);
3677
+ return 0;
3678
+ }
3679
+ function emitTemplateList(result, ctx, rc) {
3680
+ if (rc.signal?.aborted) {
3681
+ throw new AuthError("AUTH_FLOW_CANCELLED", "Cancelled.");
3682
+ }
3683
+ if (rc.jsonMode) {
3684
+ rc.stdout.write(JSON.stringify(wrapSuccess(result, ctx)) + "\n");
3685
+ return;
3686
+ }
3687
+ renderTemplatesList(result, rc.stdout);
3688
+ }
3689
+ function emitTemplateDetail(detail, ctx, rc) {
3690
+ if (rc.signal?.aborted) {
3691
+ throw new AuthError("AUTH_FLOW_CANCELLED", "Cancelled.");
3692
+ }
3693
+ if (rc.jsonMode) {
3694
+ rc.stdout.write(JSON.stringify(wrapSuccess(detail, ctx)) + "\n");
3695
+ return;
3696
+ }
3697
+ renderTemplateDetail(detail, rc.stdout);
3698
+ }
3395
3699
 
3396
3700
  // src/index.ts
3397
3701
  var __filename2 = fileURLToPath(import.meta.url);