@zapier/zapier-sdk-cli 0.4.4 → 0.6.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.
@@ -13,7 +13,9 @@ function formatJsonOutput(data) {
13
13
  if (data &&
14
14
  typeof data === "object" &&
15
15
  !Array.isArray(data) &&
16
- (data.success !== undefined || data.id || data.status)) {
16
+ (data.success !== undefined ||
17
+ data.id ||
18
+ data.status)) {
17
19
  console.log(chalk.green("✅ Action completed successfully!\n"));
18
20
  }
19
21
  // Use util.inspect for colored output
@@ -106,12 +108,13 @@ function methodNameToCliCommand(methodName) {
106
108
  // ============================================================================
107
109
  export function generateCliCommands(program, sdk) {
108
110
  // Check if SDK has registry
109
- if (!sdk.__registry) {
111
+ if (typeof sdk.getRegistry !== "function") {
110
112
  console.error("SDK registry not available");
111
113
  return;
112
114
  }
113
- // Generate one flat command for each function in the registry
114
- sdk.__registry.forEach((fnInfo) => {
115
+ const registry = sdk.getRegistry();
116
+ // Create all commands first
117
+ registry.functions.forEach((fnInfo) => {
115
118
  if (!fnInfo.inputSchema) {
116
119
  console.warn(`Schema not found for ${fnInfo.name}`);
117
120
  return;
@@ -121,6 +124,57 @@ export function generateCliCommands(program, sdk) {
121
124
  const config = createCommandConfig(cliCommandName, fnInfo.name, fnInfo.inputSchema, sdk);
122
125
  addCommand(program, cliCommandName, config);
123
126
  });
127
+ // Override the help display to show commands grouped by category
128
+ program.configureHelp({
129
+ formatHelp: (cmd, helper) => {
130
+ const helpWidth = helper.helpWidth || 80;
131
+ let output = helper.commandUsage(cmd) + "\n";
132
+ if (cmd.description()) {
133
+ output += helper.wrap(cmd.description(), helpWidth, 0) + "\n";
134
+ }
135
+ // Add options section
136
+ const options = helper.visibleOptions(cmd);
137
+ if (options.length > 0) {
138
+ output += "\nOptions:\n";
139
+ const longestOptionLength = Math.max(...options.map((opt) => helper.optionTerm(opt).length));
140
+ options.forEach((option) => {
141
+ const term = helper.optionTerm(option);
142
+ const padding = " ".repeat(Math.max(2, longestOptionLength - term.length + 4));
143
+ output += ` ${term}${padding}${helper.optionDescription(option)}\n`;
144
+ });
145
+ }
146
+ // Add categorized commands section
147
+ const commands = helper.visibleCommands(cmd);
148
+ if (commands.length > 0) {
149
+ output += "\nCommands:\n";
150
+ // Collect all SDK commands that belong to categories
151
+ const categorizedCommands = new Set();
152
+ // Group SDK commands by categories
153
+ registry.categories.forEach((category) => {
154
+ const categoryCommands = commands.filter((command) => category.functions.some((functionName) => {
155
+ const cliCommandName = methodNameToCliCommand(functionName);
156
+ return command.name() === cliCommandName;
157
+ }));
158
+ if (categoryCommands.length > 0) {
159
+ output += `\n ${category.titlePlural}:\n`;
160
+ categoryCommands.forEach((command) => {
161
+ output += ` ${helper.subcommandTerm(command)}\n`;
162
+ categorizedCommands.add(command.name());
163
+ });
164
+ }
165
+ });
166
+ // Add any remaining commands that aren't part of SDK categories
167
+ const otherCommands = commands.filter((command) => !categorizedCommands.has(command.name()));
168
+ if (otherCommands.length > 0) {
169
+ output += `\n Other:\n`;
170
+ otherCommands.forEach((command) => {
171
+ output += ` ${helper.subcommandTerm(command)}\n`;
172
+ });
173
+ }
174
+ }
175
+ return output;
176
+ },
177
+ });
124
178
  }
125
179
  function createCommandConfig(cliCommandName, sdkMethodName, schema, sdk) {
126
180
  const parameters = analyzeZodSchema(schema);
@@ -140,28 +194,33 @@ function createCommandConfig(cliCommandName, sdkMethodName, schema, sdk) {
140
194
  // Resolve missing parameters interactively using schema metadata
141
195
  const resolver = new SchemaParameterResolver();
142
196
  const resolvedParams = await resolver.resolveParameters(schema, rawParams, sdk);
143
- // Special handling for commands that write to files
144
- const hasOutputFile = resolvedParams.output;
145
- if (hasOutputFile) {
146
- // Call the SDK method for file output commands
147
- await sdk[sdkMethodName](resolvedParams);
148
- console.log(chalk.green(`✅ ${cliCommandName} completed successfully!`));
149
- console.log(chalk.gray(`Output written to: ${resolvedParams.output}`));
150
- return;
151
- }
152
197
  // Handle paginated list commands with async iteration
153
198
  if (isListCommand &&
154
199
  hasPaginationParams &&
155
200
  !shouldUseJson &&
156
201
  !hasUserSpecifiedMaxItems) {
157
202
  // Get the async iterator directly from the SDK method call
158
- const sdkIterator = sdk[sdkMethodName](resolvedParams);
203
+ const sdkObj = sdk;
204
+ const sdkIterator = await sdkObj[sdkMethodName](resolvedParams);
159
205
  await handlePaginatedListWithAsyncIteration(sdkMethodName, sdkIterator, schema);
160
206
  }
161
207
  else {
208
+ // Special handling for commands that write to files
209
+ const hasOutputFile = resolvedParams.output;
210
+ if (hasOutputFile) {
211
+ // Call the SDK method for file output commands
212
+ const sdkObj = sdk;
213
+ await sdkObj[sdkMethodName](resolvedParams);
214
+ console.log(chalk.green(`✅ ${cliCommandName} completed successfully!`));
215
+ console.log(chalk.gray(`Output written to: ${hasOutputFile}`));
216
+ return;
217
+ }
162
218
  // Call the SDK method and handle non-paginated results
163
- const result = await sdk[sdkMethodName](resolvedParams);
164
- const items = result?.data ? result.data : result;
219
+ const sdkObj = sdk;
220
+ const result = await sdkObj[sdkMethodName](resolvedParams);
221
+ const items = result?.data
222
+ ? result.data
223
+ : result;
165
224
  if (shouldUseJson) {
166
225
  console.log(JSON.stringify(items, null, 2));
167
226
  }
@@ -180,8 +239,9 @@ function createCommandConfig(cliCommandName, sdkMethodName, schema, sdk) {
180
239
  const validationErrors = JSON.parse(error.message);
181
240
  console.error(chalk.red("❌ Validation Error:"));
182
241
  validationErrors.forEach((err) => {
183
- const field = err.path?.join(".") || "unknown";
184
- console.error(chalk.yellow(` • ${field}: ${err.message}`));
242
+ const errorObj = err;
243
+ const field = errorObj?.path?.join(".") || "unknown";
244
+ console.error(chalk.yellow(` • ${field}: ${errorObj?.message || "Unknown error"}`));
185
245
  });
186
246
  console.error("\n" + chalk.dim(`Use --help to see available options`));
187
247
  }
@@ -239,7 +299,7 @@ function addCommand(program, commandName, config) {
239
299
  }
240
300
  else {
241
301
  const flagSignature = flags.join(", ") + ` <${param.type}>`;
242
- command.option(flagSignature, param.description, param.default);
302
+ command.option(flagSignature, param.description || "", param.default);
243
303
  }
244
304
  }
245
305
  });
@@ -415,10 +475,11 @@ function formatNonPaginatedResults(result, requestedMaxItems, userSpecifiedMaxIt
415
475
  function formatItemsGeneric(items) {
416
476
  // Fallback formatting for items without schema metadata
417
477
  items.forEach((item, index) => {
418
- const name = item.title || item.name || item.key || item.id || "Item";
419
- console.log(`${chalk.gray(`${index + 1}.`)} ${chalk.cyan(name)}`);
420
- if (item.description) {
421
- console.log(` ${chalk.dim(item.description)}`);
478
+ const itemObj = item;
479
+ const name = itemObj?.name || itemObj?.key || itemObj?.id || "Item";
480
+ console.log(`${chalk.gray(`${index + 1}.`)} ${chalk.cyan(String(name))}`);
481
+ if (itemObj?.description) {
482
+ console.log(` ${chalk.dim(String(itemObj.description))}`);
422
483
  }
423
484
  console.log();
424
485
  });
@@ -1,7 +1,7 @@
1
1
  declare const log: {
2
- info: (message: string, ...args: any[]) => void;
3
- error: (message: string, ...args: any[]) => void;
4
- success: (message: string, ...args: any[]) => void;
5
- warn: (message: string, ...args: any[]) => void;
2
+ info: (message: string, ...args: unknown[]) => void;
3
+ error: (message: string, ...args: unknown[]) => void;
4
+ success: (message: string, ...args: unknown[]) => void;
5
+ warn: (message: string, ...args: unknown[]) => void;
6
6
  };
7
7
  export default log;
@@ -1,7 +1,7 @@
1
1
  import { z } from "zod";
2
2
  import { ZapierSdk } from "@zapier/zapier-sdk";
3
3
  export declare class SchemaParameterResolver {
4
- resolveParameters(schema: z.ZodSchema, providedParams: any, sdk: ZapierSdk): Promise<any>;
4
+ resolveParameters(schema: z.ZodSchema, providedParams: unknown, sdk: ZapierSdk): Promise<unknown>;
5
5
  private extractParametersFromSchema;
6
6
  private analyzeFieldSchema;
7
7
  private createResolvableParameter;
@@ -55,7 +55,7 @@ export class SchemaParameterResolver {
55
55
  }
56
56
  return parseResult.data;
57
57
  }
58
- // 2. Resolve functionally required parameters and their dependencies first
58
+ // 2. Resolve functionally required parameters first
59
59
  const resolvedParams = { ...providedParams };
60
60
  const context = {
61
61
  sdk,
@@ -214,28 +214,31 @@ export class SchemaParameterResolver {
214
214
  throw new Error(`No resolver found for parameter: ${param.name}`);
215
215
  }
216
216
  console.log(chalk.blue(`\n🔍 Resolving ${param.name}...`));
217
- if (resolver.type === "static") {
217
+ const typedResolver = resolver;
218
+ if (typedResolver.type === "static") {
218
219
  // Static resolver - just prompt for input
219
220
  const promptConfig = {
220
- type: resolver.inputType === "password" ? "password" : "input",
221
+ type: typedResolver.inputType === "password" ? "password" : "input",
221
222
  name: param.name,
222
223
  message: `Enter ${param.name}:`,
223
- ...(resolver.placeholder && { default: resolver.placeholder }),
224
+ ...(typedResolver.placeholder && {
225
+ default: typedResolver.placeholder,
226
+ }),
224
227
  };
225
228
  const answers = await inquirer.prompt([promptConfig]);
226
229
  return answers[param.name];
227
230
  }
228
- else if (resolver.type === "dynamic") {
231
+ else if (typedResolver.type === "dynamic") {
229
232
  // Dynamic resolver - fetch options and prompt for selection
230
233
  try {
231
234
  // Only show "Fetching..." for required parameters that typically have many options
232
235
  if (param.isRequired && param.name !== "authenticationId") {
233
236
  console.log(chalk.gray(`Fetching options for ${param.name}...`));
234
237
  }
235
- const items = await resolver.fetch(context.sdk, context.resolvedParams);
238
+ const items = await typedResolver.fetch(context.sdk, context.resolvedParams);
236
239
  // Let the resolver's prompt handle empty lists (e.g., authenticationId can show "skip authentication")
237
240
  const safeItems = items || [];
238
- const promptConfig = resolver.prompt(safeItems, context.resolvedParams);
241
+ const promptConfig = typedResolver.prompt(safeItems, context.resolvedParams);
239
242
  const answers = await inquirer.prompt([promptConfig]);
240
243
  return answers[param.name];
241
244
  }
@@ -244,13 +247,14 @@ export class SchemaParameterResolver {
244
247
  throw error;
245
248
  }
246
249
  }
247
- else if (resolver.type === "fields") {
250
+ else if (typedResolver.type === "fields") {
248
251
  // Fields resolver - fetch field definitions and prompt for each input with recursive field resolution
249
252
  return await this.resolveFieldsRecursively(resolver, context, param);
250
253
  }
251
254
  throw new Error(`Unknown resolver type for ${param.name}`);
252
255
  }
253
256
  async resolveFieldsRecursively(resolver, context, param) {
257
+ const typedResolver = resolver;
254
258
  const inputs = {};
255
259
  let processedFieldKeys = new Set();
256
260
  let iteration = 0;
@@ -266,7 +270,7 @@ export class SchemaParameterResolver {
266
270
  },
267
271
  };
268
272
  console.log(chalk.gray(`Fetching input fields for ${param.name}${iteration > 1 ? ` (iteration ${iteration})` : ""}...`));
269
- const fields = await resolver.fetch(updatedContext.sdk, updatedContext.resolvedParams);
273
+ const fields = await typedResolver.fetch(updatedContext.sdk, updatedContext.resolvedParams);
270
274
  if (!fields || fields.length === 0) {
271
275
  if (iteration === 1) {
272
276
  console.log(chalk.yellow(`No input fields required for this action.`));
@@ -274,7 +278,8 @@ export class SchemaParameterResolver {
274
278
  break;
275
279
  }
276
280
  // Find new fields that we haven't processed yet
277
- const newFields = fields.filter((field) => !processedFieldKeys.has(field.key));
281
+ const newFields = fields.filter((field) => !(field.key &&
282
+ processedFieldKeys.has(field.key)));
278
283
  if (newFields.length === 0) {
279
284
  // No new fields, we're done
280
285
  break;
@@ -340,35 +345,44 @@ export class SchemaParameterResolver {
340
345
  setNestedValue(obj, path, value) {
341
346
  const lastKey = path[path.length - 1];
342
347
  const parent = path.slice(0, -1).reduce((current, key) => {
343
- if (!(key in current)) {
344
- current[key] = {};
348
+ const currentObj = current;
349
+ if (!(key in currentObj)) {
350
+ currentObj[key] = {};
345
351
  }
346
- return current[key];
352
+ return currentObj[key];
347
353
  }, obj);
348
354
  parent[lastKey] = value;
349
355
  }
350
356
  async promptForField(field, inputs) {
357
+ const fieldObj = field;
351
358
  const fieldPrompt = {
352
- type: field.type === "boolean" ? "confirm" : "input",
353
- name: field.key,
354
- message: `${field.label || field.key}${field.required ? " (required)" : " (optional)"}:`,
355
- ...(field.helpText && { prefix: chalk.gray(`ℹ ${field.helpText}\n`) }),
356
- ...(field.default && { default: field.default }),
359
+ type: fieldObj.type === "boolean" ? "confirm" : "input",
360
+ name: fieldObj.key,
361
+ message: `${fieldObj.label || fieldObj.key}${fieldObj.required ? " (required)" : " (optional)"}:`,
357
362
  };
358
- if (field.choices && field.choices.length > 0) {
363
+ if (fieldObj.helpText) {
364
+ fieldPrompt.prefix = chalk.gray(`ℹ ${fieldObj.helpText}\n`);
365
+ }
366
+ if (fieldObj.default !== undefined) {
367
+ fieldPrompt.default = fieldObj.default;
368
+ }
369
+ if (fieldObj.choices && fieldObj.choices.length > 0) {
359
370
  fieldPrompt.type = "list";
360
- fieldPrompt.choices = field.choices.map((choice) => ({
361
- name: choice.label || choice.value,
362
- value: choice.value,
363
- }));
371
+ fieldPrompt.choices = fieldObj.choices.map((choice) => {
372
+ const choiceObj = choice;
373
+ return {
374
+ name: choiceObj.label || choiceObj.value,
375
+ value: choiceObj.value,
376
+ };
377
+ });
364
378
  }
365
379
  try {
366
380
  const answer = await inquirer.prompt([fieldPrompt]);
367
- if (answer[field.key] !== undefined && answer[field.key] !== "") {
368
- inputs[field.key] = answer[field.key];
381
+ if (answer[fieldObj.key] !== undefined && answer[fieldObj.key] !== "") {
382
+ inputs[fieldObj.key] = answer[fieldObj.key];
369
383
  }
370
- else if (field.required) {
371
- throw new Error(`Required field ${field.key} cannot be empty`);
384
+ else if (fieldObj.required) {
385
+ throw new Error(`Required field ${fieldObj.key} cannot be empty`);
372
386
  }
373
387
  }
374
388
  catch (error) {
@@ -380,8 +394,9 @@ export class SchemaParameterResolver {
380
394
  }
381
395
  }
382
396
  isUserCancellation(error) {
383
- return (error?.name === "ExitPromptError" ||
384
- error?.message?.includes("User force closed") ||
385
- error?.isTTYError);
397
+ const errorObj = error;
398
+ return (errorObj?.name === "ExitPromptError" ||
399
+ errorObj?.message?.includes("User force closed") ||
400
+ errorObj?.isTTYError === true);
386
401
  }
387
402
  }
@@ -1,2 +1,2 @@
1
1
  import { z } from "zod";
2
- export declare function formatItemsFromSchema(inputSchema: z.ZodType, items: any[]): void;
2
+ export declare function formatItemsFromSchema(inputSchema: z.ZodType, items: unknown[]): void;
@@ -1,6 +1,7 @@
1
1
  import chalk from "chalk";
2
2
  function getFormatMetadata(schema) {
3
- return schema?._def?.formatMeta;
3
+ return schema?._def
4
+ ?.formatMeta;
4
5
  }
5
6
  function getOutputSchema(schema) {
6
7
  return schema?._def?.outputSchema;
@@ -61,10 +62,11 @@ function applyStyle(value, style) {
61
62
  function formatItemsGeneric(items) {
62
63
  // Fallback formatting for items without schema metadata
63
64
  items.forEach((item, index) => {
64
- const name = item.title || item.name || item.key || item.id || "Item";
65
+ const itemObj = item;
66
+ const name = itemObj.title || itemObj.name || itemObj.key || itemObj.id || "Item";
65
67
  console.log(`${chalk.gray(`${index + 1}.`)} ${chalk.cyan(name)}`);
66
- if (item.description) {
67
- console.log(` ${chalk.dim(item.description)}`);
68
+ if (itemObj.description) {
69
+ console.log(` ${chalk.dim(itemObj.description)}`);
68
70
  }
69
71
  console.log();
70
72
  });