@zapier/zapier-sdk-cli 0.5.0 → 0.6.1

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zapier/zapier-sdk-cli",
3
- "version": "0.5.0",
3
+ "version": "0.6.1",
4
4
  "description": "Command line interface for Zapier SDK",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -31,9 +31,9 @@
31
31
  "ora": "^8.2.0",
32
32
  "pkce-challenge": "^5.0.0",
33
33
  "zod": "^3.25.67",
34
- "@zapier/zapier-sdk": "0.5.2",
34
+ "@zapier/zapier-sdk": "0.6.1",
35
35
  "@zapier/zapier-sdk-cli-login": "0.3.2",
36
- "@zapier/zapier-sdk-mcp": "0.1.4"
36
+ "@zapier/zapier-sdk-mcp": "0.2.1"
37
37
  },
38
38
  "devDependencies": {
39
39
  "@types/express": "^5.0.3",
package/src/cli.test.ts CHANGED
@@ -8,8 +8,7 @@ describe("CLI", () => {
8
8
  cwd: __dirname + "/..",
9
9
  });
10
10
 
11
- expect(result).toContain("Usage:");
12
- expect(result).toContain("Commands:");
13
11
  expect(result).toContain("Options:");
12
+ expect(result).toContain("Commands:");
14
13
  });
15
14
  });
package/src/cli.ts CHANGED
@@ -16,7 +16,7 @@ const program = new Command();
16
16
 
17
17
  program
18
18
  .name("zapier-sdk")
19
- .description("CLI for Zapier SDK - Commands auto-generated from SDK schemas")
19
+ .description("CLI for Zapier SDK")
20
20
  .version("1.0.0")
21
21
  .option("--debug", "Enable debug logging");
22
22
 
@@ -75,9 +75,10 @@ export function createGenerateTypesCommand(): Command {
75
75
  options,
76
76
  );
77
77
 
78
- // Create SDK instance
79
- const sdk = createZapierSdk();
80
-
78
+ // Create SDK instance with manifest path if provided
79
+ const sdk = createZapierSdk({
80
+ manifestPath: rawParams.lockFilePath as string | undefined,
81
+ });
81
82
  // Resolve missing parameters interactively using schema metadata
82
83
  const resolver = new SchemaParameterResolver();
83
84
  const resolvedParams = await resolver.resolveParameters(
@@ -64,24 +64,50 @@ export async function generateTypes(
64
64
  if (authenticationId) {
65
65
  for (const action of actions) {
66
66
  try {
67
+ // Check to see if the appKey is in the manifest
68
+ const manifestEntry = sdk.getContext().getManifestEntry(appKey);
69
+
67
70
  const fieldsResult = await sdk.listInputFields({
68
- appKey: action.app_key,
71
+ // If the appKey is in the manifest, use the appKey so that the types are consistent with the manifest's version, otherwise use the action.app_key
72
+ appKey: manifestEntry ? appKey : action.app_key,
69
73
  actionKey: action.key,
70
- actionType: action.action_type as any,
74
+ actionType: action.action_type,
71
75
  authenticationId: authenticationId,
72
76
  });
73
77
 
74
- const fields = fieldsResult.data; // Direct array result
75
- actionsWithFields.push({ ...action, inputFields: fields } as any);
78
+ const fields = fieldsResult.data.map((field: unknown): ActionField => {
79
+ const fieldObj = field as {
80
+ is_required?: boolean;
81
+ required?: boolean;
82
+ [key: string]: unknown;
83
+ };
84
+ return {
85
+ ...fieldObj,
86
+ required: fieldObj.is_required || fieldObj.required || false,
87
+ } as ActionField;
88
+ });
89
+ actionsWithFields.push({
90
+ ...action,
91
+ inputFields: fields,
92
+ name: action.title || action.key,
93
+ });
76
94
  } catch {
77
95
  // If we can't get fields for an action, include it without fields
78
- actionsWithFields.push({ ...action, inputFields: [] } as any);
96
+ actionsWithFields.push({
97
+ ...action,
98
+ inputFields: [],
99
+ name: action.title || action.key,
100
+ });
79
101
  }
80
102
  }
81
103
  } else {
82
104
  // Convert actions to have empty input fields (will generate generic types)
83
105
  actions.forEach((action: ActionItem) => {
84
- actionsWithFields.push({ ...action, inputFields: [] } as any);
106
+ actionsWithFields.push({
107
+ ...action,
108
+ inputFields: [],
109
+ name: action.title || action.key,
110
+ });
85
111
  });
86
112
  }
87
113
 
@@ -17,6 +17,10 @@ export const GenerateTypesSchema = z
17
17
  debug: DebugPropertySchema.describe(
18
18
  "Enable debug logging during generation",
19
19
  ),
20
+ lockFilePath: z
21
+ .string()
22
+ .optional()
23
+ .describe("Path to the .zapierrc lock file (defaults to .zapierrc)"),
20
24
  })
21
25
  .describe("Generate TypeScript SDK code for a specific app");
22
26
 
@@ -2,6 +2,7 @@ import open from "open";
2
2
  import crypto from "node:crypto";
3
3
  import express from "express";
4
4
  import pkceChallenge from "pkce-challenge";
5
+ import type { Socket } from "net";
5
6
 
6
7
  import {
7
8
  AUTH_MODE_HEADER,
@@ -86,7 +87,7 @@ const login = async (timeoutMs: number = LOGIN_TIMEOUT_MS): Promise<string> => {
86
87
  const server = app.listen(availablePort);
87
88
 
88
89
  // Track connections to force close them if needed
89
- const connections = new Set<any>();
90
+ const connections = new Set<Socket>();
90
91
  server.on("connection", (conn) => {
91
92
  connections.add(conn);
92
93
  conn.on("close", () => connections.delete(conn));
@@ -157,13 +157,15 @@ function methodNameToCliCommand(methodName: string): string {
157
157
 
158
158
  export function generateCliCommands(program: Command, sdk: ZapierSdk): void {
159
159
  // Check if SDK has registry
160
- if (!sdk.__registry) {
160
+ if (typeof sdk.getRegistry !== "function") {
161
161
  console.error("SDK registry not available");
162
162
  return;
163
163
  }
164
164
 
165
- // Generate one flat command for each function in the registry
166
- sdk.__registry.forEach((fnInfo) => {
165
+ const registry = sdk.getRegistry();
166
+
167
+ // Create all commands first
168
+ registry.functions.forEach((fnInfo) => {
167
169
  if (!fnInfo.inputSchema) {
168
170
  console.warn(`Schema not found for ${fnInfo.name}`);
169
171
  return;
@@ -181,6 +183,75 @@ export function generateCliCommands(program: Command, sdk: ZapierSdk): void {
181
183
 
182
184
  addCommand(program, cliCommandName, config);
183
185
  });
186
+
187
+ // Override the help display to show commands grouped by category
188
+ program.configureHelp({
189
+ formatHelp: (cmd, helper) => {
190
+ const helpWidth = helper.helpWidth || 80;
191
+
192
+ let output = helper.commandUsage(cmd) + "\n";
193
+
194
+ if (cmd.description()) {
195
+ output += helper.wrap(cmd.description(), helpWidth, 0) + "\n";
196
+ }
197
+
198
+ // Add options section
199
+ const options = helper.visibleOptions(cmd);
200
+ if (options.length > 0) {
201
+ output += "\nOptions:\n";
202
+ const longestOptionLength = Math.max(
203
+ ...options.map((opt) => helper.optionTerm(opt).length),
204
+ );
205
+ options.forEach((option) => {
206
+ const term = helper.optionTerm(option);
207
+ const padding = " ".repeat(
208
+ Math.max(2, longestOptionLength - term.length + 4),
209
+ );
210
+ output += ` ${term}${padding}${helper.optionDescription(option)}\n`;
211
+ });
212
+ }
213
+
214
+ // Add categorized commands section
215
+ const commands = helper.visibleCommands(cmd);
216
+ if (commands.length > 0) {
217
+ output += "\nCommands:\n";
218
+
219
+ // Collect all SDK commands that belong to categories
220
+ const categorizedCommands = new Set<string>();
221
+
222
+ // Group SDK commands by categories
223
+ registry.categories.forEach((category) => {
224
+ const categoryCommands = commands.filter((command) =>
225
+ category.functions.some((functionName) => {
226
+ const cliCommandName = methodNameToCliCommand(functionName);
227
+ return command.name() === cliCommandName;
228
+ }),
229
+ );
230
+
231
+ if (categoryCommands.length > 0) {
232
+ output += `\n ${category.titlePlural}:\n`;
233
+ categoryCommands.forEach((command) => {
234
+ output += ` ${helper.subcommandTerm(command)}\n`;
235
+ categorizedCommands.add(command.name());
236
+ });
237
+ }
238
+ });
239
+
240
+ // Add any remaining commands that aren't part of SDK categories
241
+ const otherCommands = commands.filter(
242
+ (command) => !categorizedCommands.has(command.name()),
243
+ );
244
+ if (otherCommands.length > 0) {
245
+ output += `\n Other:\n`;
246
+ otherCommands.forEach((command) => {
247
+ output += ` ${helper.subcommandTerm(command)}\n`;
248
+ });
249
+ }
250
+ }
251
+
252
+ return output;
253
+ },
254
+ });
184
255
  }
185
256
 
186
257
  function createCommandConfig(
@@ -306,7 +306,7 @@ export class SchemaParameterResolver {
306
306
  private async resolveParameter(
307
307
  param: ResolvableParameter,
308
308
  context: ResolverContext,
309
- ): Promise<any> {
309
+ ): Promise<unknown> {
310
310
  const resolver = getResolver(param.name);
311
311
  if (!resolver) {
312
312
  throw new Error(`No resolver found for parameter: ${param.name}`);
@@ -314,39 +314,55 @@ export class SchemaParameterResolver {
314
314
 
315
315
  console.log(chalk.blue(`\n🔍 Resolving ${param.name}...`));
316
316
 
317
- if (resolver.type === "static") {
317
+ const typedResolver = resolver as {
318
+ type: string;
319
+ inputType?: string;
320
+ placeholder?: string;
321
+ fetch?: Function;
322
+ prompt?: Function;
323
+ };
324
+
325
+ if (typedResolver.type === "static") {
318
326
  // Static resolver - just prompt for input
319
327
  const promptConfig = {
320
- type: resolver.inputType === "password" ? "password" : "input",
328
+ type: typedResolver.inputType === "password" ? "password" : "input",
321
329
  name: param.name,
322
330
  message: `Enter ${param.name}:`,
323
- ...(resolver.placeholder && { default: resolver.placeholder }),
331
+ ...(typedResolver.placeholder && {
332
+ default: typedResolver.placeholder,
333
+ }),
324
334
  };
325
335
 
326
336
  const answers = await inquirer.prompt([promptConfig as any]);
327
337
  return answers[param.name];
328
- } else if (resolver.type === "dynamic") {
338
+ } else if (typedResolver.type === "dynamic") {
329
339
  // Dynamic resolver - fetch options and prompt for selection
330
340
  try {
331
341
  // Only show "Fetching..." for required parameters that typically have many options
332
342
  if (param.isRequired && param.name !== "authenticationId") {
333
343
  console.log(chalk.gray(`Fetching options for ${param.name}...`));
334
344
  }
335
- const items = await resolver.fetch(context.sdk, context.resolvedParams);
345
+ const items = await typedResolver.fetch!(
346
+ context.sdk,
347
+ context.resolvedParams,
348
+ );
336
349
 
337
350
  // Let the resolver's prompt handle empty lists (e.g., authenticationId can show "skip authentication")
338
351
  const safeItems = items || [];
339
- const promptConfig = resolver.prompt(safeItems, context.resolvedParams);
352
+ const promptConfig = typedResolver.prompt!(
353
+ safeItems,
354
+ context.resolvedParams,
355
+ );
340
356
  const answers = await inquirer.prompt([promptConfig as any]);
341
357
  return answers[param.name];
342
358
  } catch (error) {
343
359
  // Let the main CLI error handler display user-friendly errors
344
360
  throw error;
345
361
  }
346
- } else if ((resolver as any).type === "fields") {
362
+ } else if (typedResolver.type === "fields") {
347
363
  // Fields resolver - fetch field definitions and prompt for each input with recursive field resolution
348
364
  return await this.resolveFieldsRecursively(
349
- resolver as any,
365
+ resolver as unknown,
350
366
  context,
351
367
  param,
352
368
  );
@@ -356,14 +372,12 @@ export class SchemaParameterResolver {
356
372
  }
357
373
 
358
374
  private async resolveFieldsRecursively(
359
- resolver: {
360
- fetch: (sdk: unknown, params: unknown) => Promise<unknown[]>;
361
- prompt: (items: unknown[], params: unknown) => unknown;
362
- },
375
+ resolver: unknown,
363
376
  context: ResolverContext,
364
377
  param: ResolvableParameter,
365
- ): Promise<Record<string, any>> {
366
- const inputs: Record<string, any> = {};
378
+ ): Promise<Record<string, unknown>> {
379
+ const typedResolver = resolver as { fetch: Function };
380
+ const inputs: Record<string, unknown> = {};
367
381
  let processedFieldKeys = new Set<string>();
368
382
  let iteration = 0;
369
383
  const maxIterations = 5; // Prevent infinite loops
@@ -386,7 +400,7 @@ export class SchemaParameterResolver {
386
400
  ),
387
401
  );
388
402
 
389
- const fields = await resolver.fetch(
403
+ const fields = await typedResolver.fetch(
390
404
  updatedContext.sdk,
391
405
  updatedContext.resolvedParams,
392
406
  );
@@ -516,7 +530,7 @@ export class SchemaParameterResolver {
516
530
 
517
531
  private async promptForField(
518
532
  field: unknown,
519
- inputs: Record<string, any>,
533
+ inputs: Record<string, unknown>,
520
534
  ): Promise<void> {
521
535
  const fieldObj = field as {
522
536
  type?: string;
@@ -13,15 +13,16 @@ interface FormattedItem {
13
13
  }
14
14
 
15
15
  interface FormatMetadata {
16
- format: (item: any) => FormattedItem;
16
+ format: (item: unknown) => FormattedItem;
17
17
  }
18
18
 
19
- function getFormatMetadata(schema: any): FormatMetadata | undefined {
20
- return schema?._def?.formatMeta;
19
+ function getFormatMetadata(schema: unknown): FormatMetadata | undefined {
20
+ return (schema as { _def?: { formatMeta?: FormatMetadata } })?._def
21
+ ?.formatMeta;
21
22
  }
22
23
 
23
- function getOutputSchema(schema: any): any {
24
- return schema?._def?.outputSchema;
24
+ function getOutputSchema(schema: unknown): unknown {
25
+ return (schema as { _def?: { outputSchema?: unknown } })?._def?.outputSchema;
25
26
  }
26
27
 
27
28
  // ============================================================================
@@ -30,7 +31,7 @@ function getOutputSchema(schema: any): any {
30
31
 
31
32
  export function formatItemsFromSchema(
32
33
  inputSchema: z.ZodType,
33
- items: any[],
34
+ items: unknown[],
34
35
  ): void {
35
36
  // Get the output schema and its format metadata
36
37
  const outputSchema = getOutputSchema(inputSchema);
@@ -54,7 +55,7 @@ export function formatItemsFromSchema(
54
55
  }
55
56
 
56
57
  function formatSingleItem(
57
- item: any,
58
+ item: unknown,
58
59
  index: number,
59
60
  formatMeta: FormatMetadata,
60
61
  ): void {
@@ -93,13 +94,21 @@ function applyStyle(value: string, style: string): string {
93
94
  }
94
95
  }
95
96
 
96
- function formatItemsGeneric(items: any[]): void {
97
+ function formatItemsGeneric(items: unknown[]): void {
97
98
  // Fallback formatting for items without schema metadata
98
99
  items.forEach((item, index) => {
99
- const name = item.title || item.name || item.key || item.id || "Item";
100
+ const itemObj = item as {
101
+ title?: string;
102
+ name?: string;
103
+ key?: string;
104
+ id?: string;
105
+ description?: string;
106
+ };
107
+ const name =
108
+ itemObj.title || itemObj.name || itemObj.key || itemObj.id || "Item";
100
109
  console.log(`${chalk.gray(`${index + 1}.`)} ${chalk.cyan(name)}`);
101
- if (item.description) {
102
- console.log(` ${chalk.dim(item.description)}`);
110
+ if (itemObj.description) {
111
+ console.log(` ${chalk.dim(itemObj.description)}`);
103
112
  }
104
113
  console.log();
105
114
  });