@zapier/zapier-sdk-cli 0.34.11 → 0.35.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.cjs CHANGED
@@ -371,9 +371,10 @@ var loginPlugin = ({
371
371
  const user = await cliLogin.getLoggedInUser();
372
372
  context.eventEmission.emit(
373
373
  "platform.sdk.ApplicationLifecycleEvent",
374
- zapierSdk.buildApplicationLifecycleEvent({
375
- lifecycle_event_type: "login_success"
376
- })
374
+ zapierSdk.buildApplicationLifecycleEvent(
375
+ { lifecycle_event_type: "login_success" },
376
+ { customuser_id: user.customUserId, account_id: user.accountId }
377
+ )
377
378
  );
378
379
  console.log(`\u2705 Successfully logged in as ${user.email}`);
379
380
  };
@@ -383,7 +384,8 @@ var loginPlugin = ({
383
384
  meta: {
384
385
  login: {
385
386
  categories: ["account"],
386
- inputSchema: LoginSchema
387
+ inputSchema: LoginSchema,
388
+ supportsJsonOutput: false
387
389
  }
388
390
  }
389
391
  }
@@ -402,7 +404,8 @@ var logoutPlugin = () => ({
402
404
  meta: {
403
405
  logout: {
404
406
  categories: ["account"],
405
- inputSchema: LogoutSchema
407
+ inputSchema: LogoutSchema,
408
+ supportsJsonOutput: false
406
409
  }
407
410
  }
408
411
  }
@@ -2489,7 +2492,8 @@ var initPlugin = () => {
2489
2492
  meta: {
2490
2493
  init: {
2491
2494
  categories: ["utility"],
2492
- inputSchema: InitSchema
2495
+ inputSchema: InitSchema,
2496
+ supportsJsonOutput: false
2493
2497
  }
2494
2498
  }
2495
2499
  }
@@ -2507,7 +2511,7 @@ function createZapierCliSdk(options = {}) {
2507
2511
 
2508
2512
  // package.json
2509
2513
  var package_default = {
2510
- version: "0.34.11"};
2514
+ version: "0.35.0"};
2511
2515
 
2512
2516
  // src/telemetry/builders.ts
2513
2517
  function createCliBaseEvent(context = {}) {
package/dist/index.mjs CHANGED
@@ -338,9 +338,10 @@ var loginPlugin = ({
338
338
  const user = await getLoggedInUser();
339
339
  context.eventEmission.emit(
340
340
  "platform.sdk.ApplicationLifecycleEvent",
341
- buildApplicationLifecycleEvent({
342
- lifecycle_event_type: "login_success"
343
- })
341
+ buildApplicationLifecycleEvent(
342
+ { lifecycle_event_type: "login_success" },
343
+ { customuser_id: user.customUserId, account_id: user.accountId }
344
+ )
344
345
  );
345
346
  console.log(`\u2705 Successfully logged in as ${user.email}`);
346
347
  };
@@ -350,7 +351,8 @@ var loginPlugin = ({
350
351
  meta: {
351
352
  login: {
352
353
  categories: ["account"],
353
- inputSchema: LoginSchema
354
+ inputSchema: LoginSchema,
355
+ supportsJsonOutput: false
354
356
  }
355
357
  }
356
358
  }
@@ -369,7 +371,8 @@ var logoutPlugin = () => ({
369
371
  meta: {
370
372
  logout: {
371
373
  categories: ["account"],
372
- inputSchema: LogoutSchema
374
+ inputSchema: LogoutSchema,
375
+ supportsJsonOutput: false
373
376
  }
374
377
  }
375
378
  }
@@ -2456,7 +2459,8 @@ var initPlugin = () => {
2456
2459
  meta: {
2457
2460
  init: {
2458
2461
  categories: ["utility"],
2459
- inputSchema: InitSchema
2462
+ inputSchema: InitSchema,
2463
+ supportsJsonOutput: false
2460
2464
  }
2461
2465
  }
2462
2466
  }
@@ -2474,7 +2478,7 @@ function createZapierCliSdk(options = {}) {
2474
2478
 
2475
2479
  // package.json
2476
2480
  var package_default = {
2477
- version: "0.34.11"};
2481
+ version: "0.35.0"};
2478
2482
 
2479
2483
  // src/telemetry/builders.ts
2480
2484
  function createCliBaseEvent(context = {}) {
package/dist/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zapier/zapier-sdk-cli",
3
- "version": "0.34.11",
3
+ "version": "0.35.0",
4
4
  "description": "Command line interface for Zapier SDK",
5
5
  "main": "dist/index.cjs",
6
6
  "module": "dist/index.mjs",
@@ -54,6 +54,7 @@ export const initPlugin = () => {
54
54
  init: {
55
55
  categories: ["utility"],
56
56
  inputSchema: InitSchema,
57
+ supportsJsonOutput: false,
57
58
  },
58
59
  },
59
60
  },
@@ -30,9 +30,7 @@ export const loginPlugin = ({ context, }) => {
30
30
  credentials: pkceCredentials,
31
31
  });
32
32
  const user = await getLoggedInUser();
33
- context.eventEmission.emit("platform.sdk.ApplicationLifecycleEvent", buildApplicationLifecycleEvent({
34
- lifecycle_event_type: "login_success",
35
- }));
33
+ context.eventEmission.emit("platform.sdk.ApplicationLifecycleEvent", buildApplicationLifecycleEvent({ lifecycle_event_type: "login_success" }, { customuser_id: user.customUserId, account_id: user.accountId }));
36
34
  console.log(`āœ… Successfully logged in as ${user.email}`);
37
35
  };
38
36
  return {
@@ -42,6 +40,7 @@ export const loginPlugin = ({ context, }) => {
42
40
  login: {
43
41
  categories: ["account"],
44
42
  inputSchema: LoginSchema,
43
+ supportsJsonOutput: false,
45
44
  },
46
45
  },
47
46
  },
@@ -12,6 +12,7 @@ export const logoutPlugin = () => ({
12
12
  logout: {
13
13
  categories: ["account"],
14
14
  inputSchema: LogoutSchema,
15
+ supportsJsonOutput: false,
15
16
  },
16
17
  },
17
18
  },
@@ -1,13 +1,15 @@
1
1
  import { z } from "zod";
2
- import { isPositional, formatErrorMessage, ZapierError, } from "@zapier/zapier-sdk";
2
+ import { isPositional } from "@zapier/zapier-sdk";
3
3
  import { SchemaParameterResolver } from "./parameter-resolver";
4
- import { formatItemsFromSchema, formatJsonOutput } from "./schema-formatter";
5
4
  import chalk from "chalk";
6
5
  import inquirer from "inquirer";
7
- import { ZapierCliError, ZapierCliExitError } from "./errors";
6
+ import { ZapierCliError, ZapierCliExitError, ZapierCliMissingParametersError, ZapierCliValidationError, } from "./errors";
8
7
  import { SHARED_COMMAND_CLI_OPTIONS } from "./cli-options";
9
8
  import { buildCliCommandExecutedEvent } from "../telemetry/builders";
9
+ import { createJsonRenderer, createInteractiveRenderer } from "./cli-renderer";
10
+ import { toKebabCase } from "./string-utils";
10
11
  const CLI_COMMAND_EXECUTED_EVENT_SUBJECT = "platform.sdk.CliCommandExecutedEvent";
12
+ const PAGINATION_PARAM_NAMES = new Set(["maxItems", "pageSize", "cursor"]);
11
13
  // Flags whose values may contain secrets and must not appear in telemetry
12
14
  const SENSITIVE_FLAGS = [
13
15
  "--credentials",
@@ -41,6 +43,60 @@ export function sanitizeCliArguments(args) {
41
43
  }
42
44
  return sanitized;
43
45
  }
46
+ function getNormalizedResult(result, { isListCommand }) {
47
+ if (result instanceof Response) {
48
+ return { kind: "response", value: result };
49
+ }
50
+ if (isListCommand) {
51
+ const sdkResult = result;
52
+ if (!Array.isArray(sdkResult?.data)) {
53
+ throw new Error(`List command returned unexpected shape (expected { data: [] }): ${JSON.stringify(result).slice(0, 200)}`);
54
+ }
55
+ return {
56
+ kind: "list",
57
+ data: sdkResult.data,
58
+ nextCursor: sdkResult.nextCursor,
59
+ };
60
+ }
61
+ const sdkResult = result;
62
+ return {
63
+ kind: "item",
64
+ value: result !== null && typeof result === "object" && "data" in result
65
+ ? sdkResult.data
66
+ : result,
67
+ };
68
+ }
69
+ function hasItems(sdkResult) {
70
+ return (sdkResult != null &&
71
+ typeof sdkResult.items === "function");
72
+ }
73
+ function hasPages(sdkResult) {
74
+ return (sdkResult != null &&
75
+ typeof sdkResult[Symbol.asyncIterator] === "function");
76
+ }
77
+ async function* toItemsStream(sdkResult) {
78
+ if (hasItems(sdkResult)) {
79
+ yield* sdkResult.items();
80
+ return;
81
+ }
82
+ if (hasPages(sdkResult)) {
83
+ for await (const page of sdkResult) {
84
+ yield* page.data ?? [];
85
+ }
86
+ return;
87
+ }
88
+ throw new ZapierCliExitError(`SDK method returned a value that is not async-iterable. Got: ${typeof sdkResult}. ` +
89
+ `List methods must return an AsyncIterable or an object with an items() method.`, 1);
90
+ }
91
+ async function collectItemsFromResult({ sdkResult, maxItems, }) {
92
+ const items = [];
93
+ for await (const item of toItemsStream(sdkResult)) {
94
+ items.push(item);
95
+ if (maxItems !== undefined && items.length >= maxItems)
96
+ break;
97
+ }
98
+ return maxItems !== undefined ? items.slice(0, maxItems) : items;
99
+ }
44
100
  const CONFIRM_MESSAGES = {
45
101
  "create-secret": {
46
102
  messageBefore: "You are about to create a sensitive secret that will be displayed as plain text.\n" +
@@ -77,6 +133,13 @@ function emitDeprecationWarning({ cliCommandName, deprecation, }) {
77
133
  console.warn(chalk.yellow(` ${deprecation.message}`));
78
134
  console.warn();
79
135
  }
136
+ function resolveOutputMode({ isListCommand, hasPaginationParams, hasUserSpecifiedMaxItems, }) {
137
+ if (!isListCommand || !hasPaginationParams)
138
+ return "single";
139
+ if (hasUserSpecifiedMaxItems)
140
+ return "collect";
141
+ return "paginate";
142
+ }
80
143
  // ============================================================================
81
144
  // Schema Analysis
82
145
  // ============================================================================
@@ -247,13 +310,6 @@ function reconstructPositionalArgs(inputParameters, flatParams) {
247
310
  // ============================================================================
248
311
  // CLI Structure Derivation - Purely Generic
249
312
  // ============================================================================
250
- /**
251
- * Convert camelCase to kebab-case
252
- * e.g., listApps -> list-apps, generateTypes -> generate-types
253
- */
254
- function toKebabCase(str) {
255
- return str.replace(/([A-Z])/g, "-$1").toLowerCase();
256
- }
257
313
  /**
258
314
  * Convert SDK method name directly to CLI command
259
315
  * e.g., listApps -> list-apps, getApp -> get-app, generateTypes -> generate-types
@@ -348,6 +404,13 @@ function createCommandConfig(cliCommandName, functionInfo, sdk) {
348
404
  let success = true;
349
405
  let errorMessage = null;
350
406
  let resolvedParams = {};
407
+ // interactiveMode is true by default; --json disables it
408
+ const commandObj = args[args.length - 1];
409
+ const options = commandObj.opts();
410
+ const interactiveMode = !options.json;
411
+ const renderer = interactiveMode
412
+ ? createInteractiveRenderer()
413
+ : createJsonRenderer();
351
414
  try {
352
415
  // The last argument is always the command object with parsed options
353
416
  const commandObj = args[args.length - 1];
@@ -358,24 +421,24 @@ function createCommandConfig(cliCommandName, functionInfo, sdk) {
358
421
  });
359
422
  // Check if this is a list command for pagination
360
423
  const isListCommand = functionInfo.type === "list";
361
- const hasPaginationParams = parameters.some((p) => p.name === "maxItems" || p.name === "pageSize");
424
+ const hasPaginationParams = parameters.some((p) => PAGINATION_PARAM_NAMES.has(p.name));
362
425
  const hasUserSpecifiedMaxItems = "maxItems" in options && options.maxItems !== undefined;
363
- const shouldUseJson = options.json;
364
- // Convert CLI args to SDK method parameters
426
+ const outputMode = resolveOutputMode({
427
+ isListCommand,
428
+ hasPaginationParams,
429
+ hasUserSpecifiedMaxItems,
430
+ });
365
431
  const rawParams = convertCliArgsToSdkParams(parameters, args.slice(0, -1), options);
366
- // Resolve missing parameters interactively using schema metadata
367
- // (only available for inputSchema-based functions)
368
432
  if (schema && !usesInputParameters) {
369
433
  const resolver = new SchemaParameterResolver();
370
- resolvedParams = (await resolver.resolveParameters(schema, rawParams, sdk, functionInfo.name));
434
+ resolvedParams = (await resolver.resolveParameters(schema, rawParams, sdk, functionInfo.name, { interactiveMode }));
371
435
  }
372
436
  else {
373
437
  resolvedParams = rawParams;
374
438
  }
375
- // Check for confirmation before executing (skip for --json mode)
376
439
  const confirm = functionInfo.confirm;
377
440
  let confirmMessageAfter;
378
- if (confirm && !shouldUseJson) {
441
+ if (confirm && interactiveMode) {
379
442
  const confirmResult = await promptConfirm(confirm);
380
443
  if (!confirmResult.confirmed) {
381
444
  console.log(chalk.yellow("Operation cancelled."));
@@ -383,111 +446,82 @@ function createCommandConfig(cliCommandName, functionInfo, sdk) {
383
446
  }
384
447
  confirmMessageAfter = confirmResult.messageAfter;
385
448
  }
386
- // Handle paginated list commands with async iteration
387
- if (isListCommand &&
388
- hasPaginationParams &&
389
- !shouldUseJson &&
390
- !hasUserSpecifiedMaxItems) {
391
- // Get the async iterable directly from the SDK method call (don't await it! that breaks the next page behavior)
392
- const sdkObj = sdk;
393
- const sdkIterable = sdkObj[functionInfo.name](resolvedParams);
394
- await handlePaginatedListWithAsyncIteration(functionInfo.name, sdkIterable, functionInfo);
395
- }
396
- else {
397
- // Special handling for commands that write to files
398
- const hasOutputFile = resolvedParams.output;
399
- if (hasOutputFile) {
400
- const sdkObj = sdk;
401
- await sdkObj[functionInfo.name](resolvedParams);
402
- console.log(chalk.green(`āœ… ${cliCommandName} completed successfully!`));
403
- console.log(chalk.gray(`Output written to: ${hasOutputFile}`));
404
- return;
405
- }
406
- // Invoke SDK method — reconstruct positional args for inputParameters functions
407
- let result;
408
- if (usesInputParameters) {
409
- const positionalArgs = reconstructPositionalArgs(functionInfo.inputParameters, resolvedParams);
410
- const sdkObj = sdk;
411
- result = await sdkObj[functionInfo.name](...positionalArgs);
449
+ const sdkMethod = sdk[functionInfo.name];
450
+ switch (outputMode) {
451
+ case "paginate": {
452
+ const source = sdkMethod(resolvedParams);
453
+ await renderer.renderPaginatedList(source, functionInfo);
454
+ break;
412
455
  }
413
- else {
414
- const sdkObj = sdk;
415
- result = await sdkObj[functionInfo.name](resolvedParams);
456
+ case "collect": {
457
+ const maxItems = resolvedParams.maxItems;
458
+ const sdkResult = sdkMethod({ ...resolvedParams, maxItems });
459
+ const allItems = await collectItemsFromResult({
460
+ sdkResult,
461
+ maxItems,
462
+ });
463
+ renderer.renderCollectedList(allItems, {
464
+ maxItems,
465
+ userSpecifiedMaxItems: hasUserSpecifiedMaxItems,
466
+ functionInfo,
467
+ });
468
+ break;
416
469
  }
417
- // Handle Response objects by wrapping in a structured envelope
418
- if (result instanceof Response) {
419
- const response = result;
420
- let body;
421
- // Read as text first to preserve body for error messages
422
- const text = await response
423
- .text()
424
- .catch(() => "[unable to read body]");
425
- try {
426
- body = JSON.parse(text);
470
+ case "single": {
471
+ const callSdkMethod = () => {
472
+ if (usesInputParameters) {
473
+ const positionalArgs = reconstructPositionalArgs(functionInfo.inputParameters, resolvedParams);
474
+ return sdkMethod(...positionalArgs);
475
+ }
476
+ return sdkMethod(resolvedParams);
477
+ };
478
+ const outputFile = resolvedParams.output;
479
+ if (outputFile) {
480
+ await callSdkMethod();
481
+ renderer.renderItem(null, {
482
+ outputFile,
483
+ commandName: cliCommandName,
484
+ });
485
+ break;
427
486
  }
428
- catch {
429
- throw new Error(`Failed to parse response as JSON (status: ${response.status}). Body: ${text.slice(0, 500)}`);
487
+ const result = await callSdkMethod();
488
+ const normalizedResult = getNormalizedResult(result, {
489
+ isListCommand,
490
+ });
491
+ if (normalizedResult.kind === "response") {
492
+ await renderer.renderResponse(normalizedResult.value);
430
493
  }
431
- result = {
432
- statusCode: response.status,
433
- headers: Object.fromEntries(response.headers.entries()),
434
- body,
435
- };
436
- }
437
- const items = result?.data
438
- ? result.data
439
- : result;
440
- if (shouldUseJson) {
441
- console.log(JSON.stringify(items, null, 2));
442
- }
443
- else if (isListCommand) {
444
- formatNonPaginatedResults(items, resolvedParams.maxItems, hasUserSpecifiedMaxItems, shouldUseJson, functionInfo);
445
- }
446
- else {
447
- formatJsonOutput(items);
448
- }
449
- // Show message after operation completes
450
- if (confirmMessageAfter) {
451
- console.log(chalk.yellow(`\n${confirmMessageAfter}`));
494
+ else if (normalizedResult.kind === "list") {
495
+ renderer.renderCollectedList(normalizedResult.data, {
496
+ maxItems: resolvedParams.maxItems,
497
+ userSpecifiedMaxItems: hasUserSpecifiedMaxItems,
498
+ functionInfo,
499
+ });
500
+ }
501
+ else {
502
+ renderer.renderItem(normalizedResult.value);
503
+ }
504
+ if (confirmMessageAfter) {
505
+ console.log(chalk.yellow(`\n${confirmMessageAfter}`));
506
+ }
507
+ break;
452
508
  }
453
509
  }
454
510
  }
455
511
  catch (error) {
456
512
  success = false;
457
513
  errorMessage = error instanceof Error ? error.message : String(error);
458
- // Handle Zod validation errors more gracefully
459
- if (error instanceof Error && error.message.includes('"code"')) {
460
- try {
461
- const validationErrors = JSON.parse(error.message);
462
- console.error(chalk.red("āŒ Validation Error:"));
463
- validationErrors.forEach((err) => {
464
- const errorObj = err;
465
- const field = errorObj?.path?.join(".") || "unknown";
466
- console.error(chalk.yellow(` • ${field}: ${errorObj?.message || "Unknown error"}`));
467
- });
468
- console.error("\n" + chalk.dim(`Use --help to see available options`));
469
- throw new ZapierCliExitError("Validation failed", 1);
470
- }
471
- catch {
472
- console.error(chalk.red("Error:"), error instanceof Error ? error.message : String(error));
473
- throw new ZapierCliExitError(error instanceof Error ? error.message : String(error), 1);
474
- }
514
+ if (error instanceof ZapierCliMissingParametersError) {
515
+ renderer.renderError(error);
516
+ }
517
+ else if (error instanceof ZapierCliValidationError) {
518
+ renderer.renderError(error);
475
519
  }
476
520
  else if (error instanceof ZapierCliError) {
477
- // Re-throw all CLI errors as-is
478
521
  throw error;
479
522
  }
480
- else if (error instanceof ZapierError) {
481
- // Handle SDK errors with clean, user-friendly messages using our formatter
482
- const formattedMessage = formatErrorMessage(error);
483
- console.error(chalk.red("āŒ Error:"), formattedMessage);
484
- throw new ZapierCliExitError(formattedMessage, 1);
485
- }
486
523
  else {
487
- // Handle other errors
488
- const msg = error instanceof Error ? error.message : "Unknown error";
489
- console.error(chalk.red("āŒ Error:"), msg);
490
- throw new ZapierCliExitError(msg, 1);
524
+ renderer.renderError(error);
491
525
  }
492
526
  }
493
527
  finally {
@@ -522,6 +556,7 @@ function createCommandConfig(cliCommandName, functionInfo, sdk) {
522
556
  handler,
523
557
  hidden: functionInfo.categories?.includes("deprecated") ?? false,
524
558
  aliases: functionInfo.aliases,
559
+ supportsJsonOutput: functionInfo.supportsJsonOutput,
525
560
  };
526
561
  }
527
562
  // Collect function for repeatable options (e.g., -d foo -d bar → ['foo', 'bar'])
@@ -537,8 +572,7 @@ function addCommand(program, commandName, config) {
537
572
  let hasPositionalArray = false;
538
573
  // Add parameters to command
539
574
  config.parameters.forEach((param) => {
540
- // Convert camelCase to kebab-case for CLI display
541
- const kebabName = param.name.replace(/([A-Z])/g, "-$1").toLowerCase();
575
+ const kebabName = toKebabCase(param.name);
542
576
  if (param.hasResolver && param.required) {
543
577
  // Required parameters with resolvers become optional positional arguments (resolver handles prompting)
544
578
  command.argument(`[${kebabName}]`, param.description || `${kebabName} parameter`);
@@ -587,12 +621,14 @@ function addCommand(program, commandName, config) {
587
621
  }
588
622
  }
589
623
  });
590
- // Add shared command options (if not already included)
624
+ // Add shared command options (if not already included or excluded by interactive mode)
591
625
  const paramNames = new Set(config.parameters.map((p) => p.name));
592
626
  SHARED_COMMAND_CLI_OPTIONS.forEach((opt) => {
593
- if (!paramNames.has(opt.name)) {
594
- command.option(opt.flag, opt.description);
595
- }
627
+ if (paramNames.has(opt.name))
628
+ return;
629
+ if (config.supportsJsonOutput === false && opt.name === "json")
630
+ return;
631
+ command.option(opt.flag, opt.description);
596
632
  });
597
633
  command.action(config.handler);
598
634
  }
@@ -669,144 +705,3 @@ function convertValue(value, type) {
669
705
  return value;
670
706
  }
671
707
  }
672
- // ============================================================================
673
- // Pagination Handlers
674
- // ============================================================================
675
- async function handlePaginatedListWithAsyncIteration(sdkMethodName, sdkResult, functionInfo) {
676
- const itemName = getItemNameFromMethod(functionInfo);
677
- let totalShown = 0;
678
- let pageCount = 0;
679
- console.log(chalk.blue(`šŸ“‹ ${getListTitleFromMethod(sdkMethodName, functionInfo)}\n`));
680
- try {
681
- // Use async iteration to go through pages
682
- for await (const page of sdkResult) {
683
- const items = page.data || page;
684
- pageCount++;
685
- if (!Array.isArray(items)) {
686
- console.log(chalk.yellow(`No ${itemName} found.`));
687
- return;
688
- }
689
- if (items.length === 0 && pageCount === 1) {
690
- console.log(chalk.yellow(`No ${itemName} found.`));
691
- return;
692
- }
693
- if (items.length === 0) {
694
- break; // No more items
695
- }
696
- // Clear screen for subsequent pages (not the first)
697
- if (pageCount > 1) {
698
- console.clear();
699
- console.log(chalk.blue(`šŸ“‹ ${getListTitleFromMethod(sdkMethodName, functionInfo)}\n`));
700
- }
701
- // Format and display items using function info
702
- if (functionInfo && functionInfo.inputSchema) {
703
- formatItemsFromSchema(functionInfo, items, totalShown);
704
- }
705
- else {
706
- formatItemsGeneric(items, totalShown);
707
- }
708
- totalShown += items.length;
709
- console.log(chalk.green(`\nāœ… Showing ${totalShown} ${itemName} (page ${pageCount})`));
710
- // Only prompt if there's a nextCursor (more pages available)
711
- if (page.nextCursor) {
712
- const { continueReading } = await inquirer.prompt([
713
- {
714
- type: "confirm",
715
- name: "continueReading",
716
- message: `Load next page?`,
717
- default: true,
718
- },
719
- ]);
720
- if (!continueReading) {
721
- break;
722
- }
723
- }
724
- else {
725
- // No more pages available, exit gracefully
726
- break;
727
- }
728
- }
729
- console.log(chalk.gray(`\nšŸ“„ Finished browsing ${itemName}`));
730
- }
731
- catch (error) {
732
- // If the result is not async iterable, fall back to showing the first page
733
- const items = sdkResult?.data || sdkResult;
734
- if (Array.isArray(items)) {
735
- if (items.length === 0) {
736
- console.log(chalk.yellow(`No ${itemName} found.`));
737
- return;
738
- }
739
- if (functionInfo && functionInfo.inputSchema) {
740
- formatItemsFromSchema(functionInfo, items, 0);
741
- }
742
- else {
743
- formatItemsGeneric(items, 0);
744
- }
745
- console.log(chalk.green(`\nāœ… Showing ${items.length} ${itemName}`));
746
- }
747
- else {
748
- throw error;
749
- }
750
- }
751
- }
752
- function formatNonPaginatedResults(result, requestedMaxItems, userSpecifiedMaxItems, useRawJson, functionInfo) {
753
- if (!Array.isArray(result)) {
754
- if (useRawJson) {
755
- console.log(JSON.stringify(result, null, 2));
756
- }
757
- else {
758
- formatJsonOutput(result);
759
- }
760
- return;
761
- }
762
- if (useRawJson) {
763
- console.log(JSON.stringify(result, null, 2));
764
- return;
765
- }
766
- const itemName = functionInfo ? getItemNameFromMethod(functionInfo) : "items";
767
- if (result.length === 0) {
768
- console.log(chalk.yellow(`No ${itemName} found.`));
769
- return;
770
- }
771
- console.log(chalk.green(`\nāœ… Found ${result.length} ${itemName}:\n`));
772
- // Use function info for formatting
773
- if (functionInfo && functionInfo.inputSchema) {
774
- formatItemsFromSchema(functionInfo, result);
775
- }
776
- else {
777
- // Fallback to generic formatting
778
- formatItemsGeneric(result);
779
- }
780
- // Show appropriate status message
781
- if (userSpecifiedMaxItems && requestedMaxItems) {
782
- console.log(chalk.gray(`\nšŸ“„ Showing up to ${requestedMaxItems} ${itemName} (--max-items ${requestedMaxItems})`));
783
- }
784
- else {
785
- console.log(chalk.gray(`\nšŸ“„ All available ${itemName} shown`));
786
- }
787
- }
788
- function formatItemsGeneric(items, startingNumber = 0) {
789
- // Fallback formatting for items without schema metadata
790
- items.forEach((item, index) => {
791
- const itemObj = item;
792
- const name = itemObj?.name || itemObj?.key || itemObj?.id || "Item";
793
- console.log(`${chalk.gray(`${startingNumber + index + 1}.`)} ${chalk.cyan(String(name))}`);
794
- if (itemObj?.description) {
795
- console.log(` ${chalk.dim(String(itemObj.description))}`);
796
- }
797
- console.log();
798
- });
799
- }
800
- // Generic helper functions that infer from schema description or method name
801
- function getItemNameFromMethod(functionInfo) {
802
- if (functionInfo.itemType) {
803
- return `${functionInfo.itemType} items`;
804
- }
805
- return "items";
806
- }
807
- function getListTitleFromMethod(methodName, functionInfo) {
808
- if (functionInfo.itemType) {
809
- return `Available ${functionInfo.itemType} items`;
810
- }
811
- return `${methodName} items`;
812
- }