@zapier/zapier-sdk-cli 0.0.3 → 0.1.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zapier/zapier-sdk-cli",
3
- "version": "0.0.3",
3
+ "version": "0.1.0",
4
4
  "description": "Command line interface for Zapier SDK",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -25,7 +25,7 @@
25
25
  "inquirer": "^12.6.3",
26
26
  "ora": "^8.2.0",
27
27
  "zod": "^3.25.67",
28
- "@zapier/zapier-sdk": "0.0.3"
28
+ "@zapier/zapier-sdk": "0.1.0"
29
29
  },
30
30
  "devDependencies": {
31
31
  "@types/inquirer": "^9.0.8",
@@ -35,7 +35,9 @@
35
35
  },
36
36
  "scripts": {
37
37
  "test": "vitest",
38
- "build": "tsc --project tsconfig.build.json",
38
+ "build": "rm -rf dist && tsc --project tsconfig.build.json",
39
+ "clean": "rm -rf dist",
40
+ "rebuild": "pnpm clean && pnpm build",
39
41
  "dev": "tsx src/cli.ts",
40
42
  "typecheck": "tsc --project tsconfig.build.json --noEmit"
41
43
  }
package/src/cli.ts CHANGED
@@ -1,8 +1,8 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  import { Command } from "commander";
4
- import { createActionsSdk } from "@zapier/zapier-sdk";
5
- import { generateCliCommands, enhanceCommandHelp } from "./utils/cli-generator";
4
+ import { createZapierSdk } from "@zapier/zapier-sdk";
5
+ import { generateCliCommands } from "./utils/cli-generator";
6
6
 
7
7
  const program = new Command();
8
8
 
@@ -18,7 +18,7 @@ const isDebugMode =
18
18
 
19
19
  // Create SDK instance for CLI operations
20
20
  // Auth will be resolved from environment variables or command options
21
- const sdk = createActionsSdk({
21
+ const sdk = createZapierSdk({
22
22
  // Token will be picked up from ZAPIER_TOKEN env var or provided via options
23
23
  debug: isDebugMode,
24
24
  });
@@ -26,7 +26,4 @@ const sdk = createActionsSdk({
26
26
  // Generate CLI commands from SDK schemas
27
27
  generateCliCommands(program, sdk);
28
28
 
29
- // Add enhanced help information
30
- enhanceCommandHelp(program);
31
-
32
29
  program.parse();
@@ -1,6 +1,6 @@
1
1
  import { Command } from "commander";
2
2
  import { z } from "zod";
3
- import { ActionsSdk, SdkSchemas } from "@zapier/zapier-sdk";
3
+ import { ZapierSdk, hasResolver } from "@zapier/zapier-sdk";
4
4
  import { SchemaParameterResolver } from "./parameter-resolver";
5
5
  import { createPager } from "./pager";
6
6
  import { formatItemsFromSchema } from "./schema-formatter";
@@ -45,7 +45,7 @@ interface CliParameter {
45
45
  description?: string;
46
46
  default?: any;
47
47
  choices?: string[];
48
- resolverMeta?: any;
48
+ hasResolver?: boolean;
49
49
  }
50
50
 
51
51
  // ============================================================================
@@ -110,8 +110,6 @@ function analyzeZodField(
110
110
  }
111
111
 
112
112
  // Extract resolver metadata
113
- const resolverMeta = (schema._def as any).resolverMeta;
114
-
115
113
  return {
116
114
  name,
117
115
  type: paramType,
@@ -119,84 +117,79 @@ function analyzeZodField(
119
117
  description: schema.description,
120
118
  default: defaultValue,
121
119
  choices,
122
- resolverMeta,
120
+ hasResolver: hasResolver(name),
123
121
  };
124
122
  }
125
123
 
126
124
  // ============================================================================
127
- // CLI Command Generation
125
+ // CLI Structure Derivation - Purely Generic
128
126
  // ============================================================================
129
127
 
130
- export function generateCliCommands(program: Command, sdk: ActionsSdk): void {
131
- // Check if SdkSchemas is available
132
- if (!SdkSchemas) {
133
- console.error("SdkSchemas not available");
128
+ /**
129
+ * Convert camelCase to kebab-case
130
+ * e.g., listApps -> list-apps, generateTypes -> generate-types
131
+ */
132
+ function toKebabCase(str: string): string {
133
+ return str.replace(/([A-Z])/g, "-$1").toLowerCase();
134
+ }
135
+
136
+ /**
137
+ * Convert SDK method name directly to CLI command
138
+ * e.g., listApps -> list-apps, getApp -> get-app, generateTypes -> generate-types
139
+ */
140
+ function methodNameToCliCommand(methodName: string): string {
141
+ return toKebabCase(methodName);
142
+ }
143
+
144
+ // ============================================================================
145
+ // CLI Command Generation - Completely Generic
146
+ // ============================================================================
147
+
148
+ export function generateCliCommands(program: Command, sdk: ZapierSdk): void {
149
+ // Check if SDK has registry
150
+ if (!sdk.__registry) {
151
+ console.error("SDK registry not available");
134
152
  return;
135
153
  }
136
154
 
137
- // Generate namespace commands (apps, actions, auths, fields)
138
- Object.entries(SdkSchemas).forEach(([namespace, methods]) => {
139
- if (namespace === "generate" || namespace === "bundle") {
140
- // Handle root tools separately
155
+ // Generate one flat command for each function in the registry
156
+ sdk.__registry.forEach((fnInfo) => {
157
+ if (!fnInfo.inputSchema) {
158
+ console.warn(`Schema not found for ${fnInfo.name}`);
141
159
  return;
142
160
  }
143
161
 
144
- const namespaceCommand = program
145
- .command(namespace)
146
- .description(`${namespace} management commands`);
147
-
148
- if (typeof methods === "object" && methods !== null) {
149
- Object.entries(methods).forEach(([method, schema]) => {
150
- const config = createCommandConfig(
151
- namespace,
152
- method,
153
- schema as z.ZodSchema,
154
- sdk,
155
- );
156
- addSubCommand(namespaceCommand, method, config);
157
- });
158
- }
159
- });
162
+ // Convert methodName to kebab-case CLI command
163
+ const cliCommandName = methodNameToCliCommand(fnInfo.name);
160
164
 
161
- // Generate root tool commands
162
- if (SdkSchemas.generate) {
163
- const generateConfig = createCommandConfig(
164
- "",
165
- "generate",
166
- SdkSchemas.generate,
165
+ const config = createCommandConfig(
166
+ cliCommandName,
167
+ fnInfo.name,
168
+ fnInfo.inputSchema as z.ZodSchema,
167
169
  sdk,
168
170
  );
169
- addSubCommand(program, "generate", generateConfig);
170
- }
171
171
 
172
- if (SdkSchemas.bundle) {
173
- const bundleConfig = createCommandConfig(
174
- "",
175
- "bundle",
176
- SdkSchemas.bundle,
177
- sdk,
178
- );
179
- addSubCommand(program, "bundle", bundleConfig);
180
- }
172
+ addCommand(program, cliCommandName, config);
173
+ });
181
174
  }
182
175
 
183
176
  function createCommandConfig(
184
- namespace: string,
185
- method: string,
177
+ cliCommandName: string,
178
+ sdkMethodName: string,
186
179
  schema: z.ZodSchema,
187
- sdk: ActionsSdk,
180
+ sdk: ZapierSdk,
188
181
  ): CliCommandConfig {
189
182
  const parameters = analyzeZodSchema(schema);
190
- const description = schema.description || `${namespace} ${method} command`;
183
+ const description = schema.description || `${cliCommandName} command`;
191
184
 
192
185
  const handler = async (...args: any[]) => {
193
186
  try {
194
187
  // The last argument is always the command object with parsed options
195
- const command = args[args.length - 1];
196
- const options = command.opts();
188
+ const commandObj = args[args.length - 1];
189
+ const options = commandObj.opts();
197
190
 
198
- // Check if this is a list command with pagination support
199
- const isListCommand = method === "list";
191
+ // Check if this is a list command for pagination
192
+ const isListCommand = cliCommandName.startsWith("list-");
200
193
  const hasPaginationParams = parameters.some(
201
194
  (p) => p.name === "limit" || p.name === "offset",
202
195
  );
@@ -213,7 +206,7 @@ function createCommandConfig(
213
206
  options,
214
207
  );
215
208
 
216
- // NEW: Resolve missing parameters interactively using schema metadata
209
+ // Resolve missing parameters interactively using schema metadata
217
210
  const resolver = new SchemaParameterResolver();
218
211
  const resolvedParams = await resolver.resolveParameters(
219
212
  schema,
@@ -223,22 +216,13 @@ function createCommandConfig(
223
216
 
224
217
  if (shouldUsePaging && !shouldUseJson) {
225
218
  // Use interactive paging for list commands
226
- await handlePaginatedList(namespace, method, resolvedParams, sdk);
219
+ await handlePaginatedList(sdkMethodName, resolvedParams, sdk, schema);
227
220
  } else {
228
- // Call the appropriate SDK method with complete, validated parameters
229
- let result: any;
230
- if (namespace === "") {
231
- // Root tool (generate, bundle)
232
- result = await (sdk as any)[method](resolvedParams);
233
- } else {
234
- // Regular namespace method
235
- result = await (sdk as any)[namespace][method](resolvedParams);
236
- }
221
+ // Call the SDK method directly
222
+ const result: any = await (sdk as any)[sdkMethodName](resolvedParams);
237
223
 
238
- // Special handling for generate and bundle commands - don't output to console if writing to file
239
- const isRootCommandWithOutput =
240
- namespace === "" && (method === "generate" || method === "bundle");
241
- const hasOutputFile = isRootCommandWithOutput && resolvedParams.output;
224
+ // Special handling for commands that write to files
225
+ const hasOutputFile = resolvedParams.output;
242
226
 
243
227
  // Output result (JSON or formatted)
244
228
  if (!hasOutputFile && (shouldUseJson || !isListCommand)) {
@@ -251,25 +235,45 @@ function createCommandConfig(
251
235
  } else if (!hasOutputFile) {
252
236
  // Format list results nicely (non-paginated)
253
237
  formatNonPaginatedResults(
254
- namespace,
255
238
  result,
256
239
  resolvedParams.limit,
257
240
  hasUserSpecifiedLimit,
258
241
  shouldUseJson,
242
+ schema,
243
+ sdkMethodName,
259
244
  );
260
245
  } else if (hasOutputFile) {
261
246
  // Show success message for file output instead of printing generated content
262
- console.log(chalk.green(`āœ… ${method} completed successfully!`));
247
+ console.log(
248
+ chalk.green(`āœ… ${cliCommandName} completed successfully!`),
249
+ );
263
250
  console.log(
264
251
  chalk.gray(`Output written to: ${resolvedParams.output}`),
265
252
  );
266
253
  }
267
254
  }
268
255
  } catch (error) {
269
- console.error(
270
- "Error:",
271
- error instanceof Error ? error.message : "Unknown error",
272
- );
256
+ // Handle Zod validation errors more gracefully
257
+ if (error instanceof Error && error.message.includes('"code"')) {
258
+ try {
259
+ const validationErrors = JSON.parse(error.message);
260
+ console.error(chalk.red("āŒ Validation Error:"));
261
+ validationErrors.forEach((err: any) => {
262
+ const field = err.path?.join(".") || "unknown";
263
+ console.error(chalk.yellow(` • ${field}: ${err.message}`));
264
+ });
265
+ console.error(
266
+ "\n" + chalk.dim(`Use --help to see available options`),
267
+ );
268
+ } catch {
269
+ console.error(chalk.red("Error:"), error.message);
270
+ }
271
+ } else {
272
+ console.error(
273
+ chalk.red("Error:"),
274
+ error instanceof Error ? error.message : "Unknown error",
275
+ );
276
+ }
273
277
  process.exit(1);
274
278
  }
275
279
  };
@@ -281,32 +285,33 @@ function createCommandConfig(
281
285
  };
282
286
  }
283
287
 
284
- function addSubCommand(
285
- parentCommand: Command,
286
- name: string,
288
+ function addCommand(
289
+ program: Command,
290
+ commandName: string,
287
291
  config: CliCommandConfig,
288
292
  ): void {
289
- const command = parentCommand.command(name).description(config.description);
293
+ const command = program.command(commandName).description(config.description);
290
294
 
291
295
  // Add parameters to command
292
296
  config.parameters.forEach((param) => {
293
- if (param.resolverMeta?.resolver && param.required) {
297
+ // Convert camelCase to kebab-case for CLI display
298
+ const kebabName = param.name.replace(/([A-Z])/g, "-$1").toLowerCase();
299
+
300
+ if (param.hasResolver && param.required) {
294
301
  // Required parameters with resolvers become optional positional arguments (resolver handles prompting)
295
302
  command.argument(
296
- `[${param.name}]`,
297
- param.description || `${param.name} parameter`,
303
+ `[${kebabName}]`,
304
+ param.description || `${kebabName} parameter`,
298
305
  );
299
306
  } else if (param.required) {
300
307
  // Required parameters without resolvers become required positional arguments
301
308
  command.argument(
302
- `<${param.name}>`,
303
- param.description || `${param.name} parameter`,
309
+ `<${kebabName}>`,
310
+ param.description || `${kebabName} parameter`,
304
311
  );
305
312
  } else {
306
313
  // Optional parameters become flags (whether they have resolvers or not)
307
- const flags = [
308
- `--${param.name.replace(/([A-Z])/g, "-$1").toLowerCase()}`,
309
- ];
314
+ const flags = [`--${kebabName}`];
310
315
 
311
316
  if (param.type === "boolean") {
312
317
  command.option(flags.join(", "), param.description);
@@ -338,6 +343,7 @@ function convertCliArgsToSdkParams(
338
343
  let argIndex = 0;
339
344
  parameters.forEach((param) => {
340
345
  if (param.required && argIndex < positionalArgs.length) {
346
+ // Use the original camelCase parameter name for the SDK
341
347
  sdkParams[param.name] = convertValue(
342
348
  positionalArgs[argIndex],
343
349
  param.type,
@@ -390,13 +396,13 @@ function convertValue(value: any, type: CliParameter["type"]): any {
390
396
  // ============================================================================
391
397
 
392
398
  async function handlePaginatedList(
393
- namespace: string,
394
- method: string,
399
+ sdkMethodName: string,
395
400
  baseParams: any,
396
- sdk: ActionsSdk,
401
+ sdk: ZapierSdk,
402
+ schema: z.ZodSchema,
397
403
  ): Promise<void> {
398
404
  const limit = baseParams.limit || 20;
399
- const itemName = getItemName(namespace);
405
+ const itemName = getItemNameFromMethod(sdkMethodName);
400
406
 
401
407
  console.log(chalk.blue(`šŸ“‹ Fetching ${itemName}...`));
402
408
 
@@ -414,22 +420,16 @@ async function handlePaginatedList(
414
420
  if (items.length > 0) {
415
421
  console.clear();
416
422
  }
417
- console.log(chalk.blue(`šŸ“‹ ${getListTitle(namespace)}\n`));
423
+ console.log(chalk.blue(`šŸ“‹ ${getListTitleFromMethod(sdkMethodName)}\n`));
418
424
 
419
425
  if (items.length === 0) {
420
426
  console.log(chalk.yellow(`No ${itemName} found.`));
421
427
  return;
422
428
  }
423
429
 
424
- // Get the schema for this namespace/method to extract formatting info
425
- const schema = SdkSchemas[namespace as keyof typeof SdkSchemas];
426
- const listSchema =
427
- schema && typeof schema === "object" && "list" in schema
428
- ? schema.list
429
- : null;
430
-
431
- if (listSchema) {
432
- formatItemsFromSchema(listSchema as z.ZodType, items);
430
+ // Use schema for formatting
431
+ if (schema) {
432
+ formatItemsFromSchema(schema as z.ZodType, items);
433
433
  } else {
434
434
  // Fallback to generic formatting
435
435
  formatItemsGeneric(items);
@@ -445,7 +445,7 @@ async function handlePaginatedList(
445
445
 
446
446
  await pager.paginate(
447
447
  (params) =>
448
- (sdk as any)[namespace][method]({
448
+ (sdk as any)[sdkMethodName]({
449
449
  ...baseParams,
450
450
  ...params,
451
451
  }),
@@ -455,11 +455,12 @@ async function handlePaginatedList(
455
455
  }
456
456
 
457
457
  function formatNonPaginatedResults(
458
- namespace: string,
459
458
  result: any[],
460
459
  requestedLimit?: number,
461
460
  userSpecifiedLimit?: boolean,
462
461
  useRawJson?: boolean,
462
+ schema?: z.ZodSchema,
463
+ methodName?: string,
463
464
  ): void {
464
465
  if (!Array.isArray(result)) {
465
466
  if (useRawJson) {
@@ -475,7 +476,7 @@ function formatNonPaginatedResults(
475
476
  return;
476
477
  }
477
478
 
478
- const itemName = getItemName(namespace);
479
+ const itemName = methodName ? getItemNameFromMethod(methodName) : "items";
479
480
 
480
481
  if (result.length === 0) {
481
482
  console.log(chalk.yellow(`No ${itemName} found.`));
@@ -484,15 +485,9 @@ function formatNonPaginatedResults(
484
485
 
485
486
  console.log(chalk.green(`\nāœ… Found ${result.length} ${itemName}:\n`));
486
487
 
487
- // Get the schema for this namespace/method to extract formatting info
488
- const schema = SdkSchemas[namespace as keyof typeof SdkSchemas];
489
- const listSchema =
490
- schema && typeof schema === "object" && "list" in schema
491
- ? schema.list
492
- : null;
493
-
494
- if (listSchema) {
495
- formatItemsFromSchema(listSchema as z.ZodType, result);
488
+ // Use schema for formatting
489
+ if (schema) {
490
+ formatItemsFromSchema(schema as z.ZodType, result);
496
491
  } else {
497
492
  // Fallback to generic formatting
498
493
  formatItemsGeneric(result);
@@ -522,56 +517,23 @@ function formatItemsGeneric(items: any[]): void {
522
517
  });
523
518
  }
524
519
 
525
- function getItemName(namespace: string): string {
526
- switch (namespace) {
527
- case "apps":
528
- return "apps";
529
- case "actions":
530
- return "actions";
531
- case "auths":
532
- return "authentications";
533
- case "fields":
534
- return "fields";
535
- default:
536
- return "items";
520
+ // Generic helper functions that infer from schema description or method name
521
+ function getItemNameFromMethod(methodName: string): string {
522
+ // Extract from method name: listApps -> apps, listActions -> actions
523
+ const listMatch = methodName.match(/^list(.+)$/);
524
+ if (listMatch) {
525
+ return listMatch[1].toLowerCase();
537
526
  }
538
- }
539
527
 
540
- function getListTitle(namespace: string): string {
541
- switch (namespace) {
542
- case "apps":
543
- return "Available Apps";
544
- case "actions":
545
- return "Available Actions";
546
- case "auths":
547
- return "Available Authentications";
548
- case "fields":
549
- return "Available Fields";
550
- default:
551
- return "Available Items";
552
- }
528
+ // Fallback to generic
529
+ return "items";
553
530
  }
554
531
 
555
- // ============================================================================
556
- // Help Text Enhancement
557
- // ============================================================================
532
+ function getListTitleFromMethod(methodName: string): string {
533
+ const itemName = getItemNameFromMethod(methodName);
534
+ if (itemName === "items") return "Available Items";
558
535
 
559
- export function enhanceCommandHelp(program: Command): void {
560
- // Add custom help that shows schema-driven nature
561
- program.on("--help", () => {
562
- console.log("");
563
- console.log("Commands are automatically generated from SDK schemas.");
564
- console.log(
565
- "Each command maps directly to an SDK method with the same parameters.",
566
- );
567
- console.log("");
568
- console.log("Examples:");
569
- console.log(" zapier-sdk apps list --category=productivity --limit=10");
570
- console.log(
571
- ' zapier-sdk actions run slack search user_by_email --inputs=\'{"email":"user@example.com"}\'',
572
- );
573
- console.log(" zapier-sdk generate my-app --output=./generated/");
574
- console.log(" zsdk apps list --limit=5 # Using the shorter alias");
575
- console.log("");
576
- });
536
+ // Capitalize first letter: apps -> Apps
537
+ const capitalized = itemName.charAt(0).toUpperCase() + itemName.slice(1);
538
+ return `Available ${capitalized}`;
577
539
  }