@zapier/zapier-sdk-cli 0.3.1 → 0.4.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.
Files changed (47) hide show
  1. package/dist/cli.js +1099 -16
  2. package/dist/index.js +0 -3
  3. package/package.json +5 -3
  4. package/src/cli.ts +2 -2
  5. package/src/commands/configPath.ts +1 -1
  6. package/src/commands/index.ts +4 -4
  7. package/src/commands/login.ts +2 -2
  8. package/src/commands/logout.ts +1 -1
  9. package/src/commands/whoami.ts +2 -2
  10. package/src/utils/auth/login.ts +6 -6
  11. package/src/utils/cli-generator.ts +3 -3
  12. package/tsconfig.json +3 -3
  13. package/tsup.config.ts +17 -0
  14. package/dist/cli.d.ts +0 -2
  15. package/dist/commands/configPath.d.ts +0 -2
  16. package/dist/commands/configPath.js +0 -9
  17. package/dist/commands/index.d.ts +0 -4
  18. package/dist/commands/index.js +0 -4
  19. package/dist/commands/login.d.ts +0 -2
  20. package/dist/commands/login.js +0 -25
  21. package/dist/commands/logout.d.ts +0 -2
  22. package/dist/commands/logout.js +0 -16
  23. package/dist/commands/whoami.d.ts +0 -2
  24. package/dist/commands/whoami.js +0 -17
  25. package/dist/index.d.ts +0 -1
  26. package/dist/utils/api/client.d.ts +0 -15
  27. package/dist/utils/api/client.js +0 -27
  28. package/dist/utils/auth/login.d.ts +0 -2
  29. package/dist/utils/auth/login.js +0 -134
  30. package/dist/utils/cli-generator.d.ts +0 -3
  31. package/dist/utils/cli-generator.js +0 -388
  32. package/dist/utils/constants.d.ts +0 -5
  33. package/dist/utils/constants.js +0 -6
  34. package/dist/utils/getCallablePromise.d.ts +0 -6
  35. package/dist/utils/getCallablePromise.js +0 -14
  36. package/dist/utils/log.d.ts +0 -7
  37. package/dist/utils/log.js +0 -16
  38. package/dist/utils/pager.d.ts +0 -48
  39. package/dist/utils/pager.js +0 -137
  40. package/dist/utils/parameter-resolver.d.ts +0 -13
  41. package/dist/utils/parameter-resolver.js +0 -300
  42. package/dist/utils/schema-formatter.d.ts +0 -2
  43. package/dist/utils/schema-formatter.js +0 -71
  44. package/dist/utils/serializeAsync.d.ts +0 -2
  45. package/dist/utils/serializeAsync.js +0 -16
  46. package/dist/utils/spinner.d.ts +0 -1
  47. package/dist/utils/spinner.js +0 -13
package/dist/cli.js CHANGED
@@ -1,25 +1,1108 @@
1
1
  #!/usr/bin/env node
2
- import { Command } from "commander";
2
+
3
+ // src/cli.ts
4
+ import { Command as Command5 } from "commander";
3
5
  import { createZapierSdk } from "@zapier/zapier-sdk";
4
- import { generateCliCommands } from "./utils/cli-generator.js";
5
- import { createLoginCommand, createLogoutCommand, createWhoamiCommand, createConfigPathCommand, } from "./commands/index.js";
6
- const program = new Command();
7
- program
8
- .name("zapier-sdk")
9
- .description("CLI for Zapier SDK - Commands auto-generated from SDK schemas")
10
- .version("1.0.0")
11
- .option("--debug", "Enable debug logging");
12
- // Check for debug flag early
13
- const isDebugMode = process.env.DEBUG === "true" || process.argv.includes("--debug");
14
- // Create SDK instance - automatically handles token resolution
15
- const sdk = createZapierSdk({
16
- debug: isDebugMode,
6
+
7
+ // src/utils/cli-generator.ts
8
+ import { z as z2 } from "zod";
9
+ import { hasResolver as hasResolver2, isPositional } from "@zapier/zapier-sdk";
10
+
11
+ // src/utils/parameter-resolver.ts
12
+ import inquirer from "inquirer";
13
+ import chalk from "chalk";
14
+ import { z } from "zod";
15
+ import {
16
+ getResolver,
17
+ hasResolver,
18
+ getResolutionOrderForParams
19
+ } from "@zapier/zapier-sdk";
20
+ var SchemaParameterResolver = class {
21
+ async resolveParameters(schema, providedParams, sdk2) {
22
+ const parseResult = schema.safeParse(providedParams);
23
+ const allParams = this.extractParametersFromSchema(schema);
24
+ const resolvableParams = allParams.filter(
25
+ (param) => hasResolver(param.name)
26
+ );
27
+ const missingResolvable = resolvableParams.filter((param) => {
28
+ const hasValue = this.getNestedValue(providedParams, param.path) !== void 0;
29
+ return !hasValue;
30
+ });
31
+ const functionallyRequired = missingResolvable.filter((param) => {
32
+ if (param.isRequired) return true;
33
+ if (param.name === "inputs" || param.name === "authenticationId") {
34
+ return true;
35
+ }
36
+ return false;
37
+ });
38
+ const trulyOptional = missingResolvable.filter(
39
+ (param) => !functionallyRequired.includes(param)
40
+ );
41
+ if (parseResult.success && functionallyRequired.length === 0) {
42
+ return parseResult.data;
43
+ }
44
+ if (functionallyRequired.length === 0) {
45
+ if (!parseResult.success) {
46
+ throw parseResult.error;
47
+ }
48
+ return parseResult.data;
49
+ }
50
+ const resolvedParams = { ...providedParams };
51
+ const context = {
52
+ sdk: sdk2,
53
+ currentParams: providedParams,
54
+ resolvedParams
55
+ };
56
+ if (functionallyRequired.length > 0) {
57
+ const requiredParamNames = functionallyRequired.map((p) => p.name);
58
+ const requiredResolutionOrder = getResolutionOrderForParams(requiredParamNames);
59
+ const orderedRequiredParams = requiredResolutionOrder.map(
60
+ (paramName) => functionallyRequired.find((p) => p.name === paramName)
61
+ ).filter((param) => param !== void 0);
62
+ for (const param of orderedRequiredParams) {
63
+ try {
64
+ const value = await this.resolveParameter(param, context);
65
+ this.setNestedValue(resolvedParams, param.path, value);
66
+ context.resolvedParams = resolvedParams;
67
+ } catch (error) {
68
+ if (this.isUserCancellation(error)) {
69
+ console.log(chalk.yellow("\n\nOperation cancelled by user"));
70
+ process.exit(0);
71
+ }
72
+ console.error(chalk.red(`Failed to resolve ${param.name}:`), error);
73
+ throw error;
74
+ }
75
+ }
76
+ }
77
+ if (trulyOptional.length > 0) {
78
+ const optionalNames = trulyOptional.map((p) => p.name).join(", ");
79
+ const shouldResolveOptional = await inquirer.prompt([
80
+ {
81
+ type: "confirm",
82
+ name: "resolveOptional",
83
+ message: `Would you like to be prompted for optional parameters (${optionalNames})?`,
84
+ default: false
85
+ }
86
+ ]);
87
+ if (shouldResolveOptional.resolveOptional) {
88
+ const optionalParamNames = trulyOptional.map((p) => p.name);
89
+ const optionalResolutionOrder = getResolutionOrderForParams(optionalParamNames);
90
+ const orderedOptionalParams = optionalResolutionOrder.map((paramName) => trulyOptional.find((p) => p.name === paramName)).filter((param) => param !== void 0);
91
+ for (const param of orderedOptionalParams) {
92
+ try {
93
+ const value = await this.resolveParameter(param, context);
94
+ this.setNestedValue(resolvedParams, param.path, value);
95
+ context.resolvedParams = resolvedParams;
96
+ } catch (error) {
97
+ if (this.isUserCancellation(error)) {
98
+ console.log(chalk.yellow("\n\nOperation cancelled by user"));
99
+ process.exit(0);
100
+ }
101
+ console.error(chalk.red(`Failed to resolve ${param.name}:`), error);
102
+ throw error;
103
+ }
104
+ }
105
+ }
106
+ }
107
+ const finalResult = schema.safeParse(resolvedParams);
108
+ if (!finalResult.success) {
109
+ console.error(
110
+ chalk.red("\u274C Parameter validation failed after resolution:")
111
+ );
112
+ throw finalResult.error;
113
+ }
114
+ return finalResult.data;
115
+ }
116
+ extractParametersFromSchema(schema) {
117
+ const parameters = [];
118
+ if (schema instanceof z.ZodObject) {
119
+ const shape = schema.shape;
120
+ for (const [key, fieldSchema] of Object.entries(shape)) {
121
+ const param = this.analyzeFieldSchema(key, fieldSchema);
122
+ if (param) {
123
+ parameters.push(param);
124
+ }
125
+ }
126
+ }
127
+ return parameters;
128
+ }
129
+ analyzeFieldSchema(fieldName, fieldSchema) {
130
+ let baseSchema = fieldSchema;
131
+ let isRequired = true;
132
+ if (baseSchema instanceof z.ZodOptional) {
133
+ isRequired = false;
134
+ baseSchema = baseSchema._def.innerType;
135
+ }
136
+ if (baseSchema instanceof z.ZodDefault) {
137
+ isRequired = false;
138
+ baseSchema = baseSchema._def.innerType;
139
+ }
140
+ return this.createResolvableParameter([fieldName], baseSchema, isRequired);
141
+ }
142
+ createResolvableParameter(path, schema, isRequired) {
143
+ if (path.length === 0) return null;
144
+ const name = path[path.length - 1];
145
+ return {
146
+ name,
147
+ path,
148
+ schema,
149
+ description: schema.description,
150
+ isRequired
151
+ };
152
+ }
153
+ async resolveParameter(param, context) {
154
+ const resolver = getResolver(param.name);
155
+ if (!resolver) {
156
+ throw new Error(`No resolver found for parameter: ${param.name}`);
157
+ }
158
+ console.log(chalk.blue(`
159
+ \u{1F50D} Resolving ${param.name}...`));
160
+ if (resolver.type === "static") {
161
+ const promptConfig = {
162
+ type: resolver.inputType === "password" ? "password" : "input",
163
+ name: param.name,
164
+ message: `Enter ${param.name}:`,
165
+ ...resolver.placeholder && { default: resolver.placeholder }
166
+ };
167
+ const answers = await inquirer.prompt([promptConfig]);
168
+ return answers[param.name];
169
+ } else if (resolver.type === "dynamic") {
170
+ try {
171
+ console.log(chalk.gray(`Fetching options for ${param.name}...`));
172
+ const items = await resolver.fetch(context.sdk, context.resolvedParams);
173
+ if (!items || items.length === 0) {
174
+ throw new Error(`No options available for ${param.name}`);
175
+ }
176
+ const promptConfig = resolver.prompt(items, context.resolvedParams);
177
+ const answers = await inquirer.prompt([promptConfig]);
178
+ return answers[param.name];
179
+ } catch (error) {
180
+ console.error(
181
+ chalk.red(`Failed to fetch options for ${param.name}:`),
182
+ error instanceof Error ? error.message : error
183
+ );
184
+ throw error;
185
+ }
186
+ } else if (resolver.type === "fields") {
187
+ try {
188
+ console.log(chalk.gray(`Fetching input fields for ${param.name}...`));
189
+ const fields = await resolver.fetch(
190
+ context.sdk,
191
+ context.resolvedParams
192
+ );
193
+ if (!fields || fields.length === 0) {
194
+ console.log(
195
+ chalk.yellow(`No input fields required for this action.`)
196
+ );
197
+ return {};
198
+ }
199
+ const inputs = {};
200
+ const requiredFields = fields.filter((field) => field.required);
201
+ const optionalFields = fields.filter((field) => !field.required);
202
+ if (requiredFields.length > 0) {
203
+ console.log(
204
+ chalk.blue(
205
+ `
206
+ \u{1F4DD} Please provide values for the following input fields:`
207
+ )
208
+ );
209
+ for (const field of requiredFields) {
210
+ await this.promptForField(field, inputs);
211
+ }
212
+ }
213
+ if (optionalFields.length > 0) {
214
+ console.log(
215
+ chalk.gray(
216
+ `
217
+ There are ${optionalFields.length} optional field(s) available.`
218
+ )
219
+ );
220
+ let shouldConfigureOptional;
221
+ try {
222
+ shouldConfigureOptional = await inquirer.prompt([
223
+ {
224
+ type: "confirm",
225
+ name: "configure",
226
+ message: "Would you like to configure optional fields?",
227
+ default: false
228
+ }
229
+ ]);
230
+ } catch (error) {
231
+ if (this.isUserCancellation(error)) {
232
+ console.log(chalk.yellow("\n\nOperation cancelled by user"));
233
+ process.exit(0);
234
+ }
235
+ throw error;
236
+ }
237
+ if (shouldConfigureOptional.configure) {
238
+ console.log(chalk.cyan(`
239
+ Optional fields:`));
240
+ for (const field of optionalFields) {
241
+ await this.promptForField(field, inputs);
242
+ }
243
+ }
244
+ }
245
+ return inputs;
246
+ } catch (error) {
247
+ console.error(
248
+ chalk.red(`Failed to fetch fields for ${param.name}:`),
249
+ error instanceof Error ? error.message : error
250
+ );
251
+ throw error;
252
+ }
253
+ }
254
+ throw new Error(`Unknown resolver type for ${param.name}`);
255
+ }
256
+ getNestedValue(obj, path) {
257
+ return path.reduce((current, key) => current?.[key], obj);
258
+ }
259
+ setNestedValue(obj, path, value) {
260
+ const lastKey = path[path.length - 1];
261
+ const parent = path.slice(0, -1).reduce((current, key) => {
262
+ if (!(key in current)) {
263
+ current[key] = {};
264
+ }
265
+ return current[key];
266
+ }, obj);
267
+ parent[lastKey] = value;
268
+ }
269
+ async promptForField(field, inputs) {
270
+ const fieldPrompt = {
271
+ type: field.type === "boolean" ? "confirm" : "input",
272
+ name: field.key,
273
+ message: `${field.label || field.key}${field.required ? " (required)" : " (optional)"}:`,
274
+ ...field.helpText && { prefix: chalk.gray(`\u2139 ${field.helpText}
275
+ `) },
276
+ ...field.default && { default: field.default }
277
+ };
278
+ if (field.choices && field.choices.length > 0) {
279
+ fieldPrompt.type = "list";
280
+ fieldPrompt.choices = field.choices.map((choice) => ({
281
+ name: choice.label || choice.value,
282
+ value: choice.value
283
+ }));
284
+ }
285
+ try {
286
+ const answer = await inquirer.prompt([fieldPrompt]);
287
+ if (answer[field.key] !== void 0 && answer[field.key] !== "") {
288
+ inputs[field.key] = answer[field.key];
289
+ } else if (field.required) {
290
+ throw new Error(`Required field ${field.key} cannot be empty`);
291
+ }
292
+ } catch (error) {
293
+ if (this.isUserCancellation(error)) {
294
+ console.log(chalk.yellow("\n\nOperation cancelled by user"));
295
+ process.exit(0);
296
+ }
297
+ throw error;
298
+ }
299
+ }
300
+ isUserCancellation(error) {
301
+ return error?.name === "ExitPromptError" || error?.message?.includes("User force closed") || error?.isTTYError;
302
+ }
303
+ };
304
+
305
+ // src/utils/pager.ts
306
+ import inquirer2 from "inquirer";
307
+ import chalk2 from "chalk";
308
+ var Pager = class {
309
+ constructor(options = {}) {
310
+ this.allItems = [];
311
+ this.currentOffset = 0;
312
+ this.pageSize = options.pageSize || 20;
313
+ this.showPrompt = options.showPrompt !== false;
314
+ this.itemName = options.itemName || "items";
315
+ }
316
+ /**
317
+ * Fetch and display paginated results with interactive loading
318
+ */
319
+ async paginate(fetchFunction, baseParams, displayFunction) {
320
+ let hasMore = true;
321
+ let totalAvailable;
322
+ while (hasMore) {
323
+ const params = {
324
+ ...baseParams,
325
+ limit: this.pageSize,
326
+ offset: this.currentOffset
327
+ };
328
+ try {
329
+ const items = await fetchFunction(params);
330
+ if (items.length === 0) {
331
+ displayFunction(this.allItems, this.allItems.length, totalAvailable);
332
+ hasMore = false;
333
+ break;
334
+ }
335
+ const pagination = items.__pagination;
336
+ if (pagination && pagination.count) {
337
+ totalAvailable = pagination.count;
338
+ hasMore = pagination.hasNext;
339
+ } else {
340
+ hasMore = items.length >= this.pageSize;
341
+ }
342
+ this.allItems.push(...items);
343
+ this.currentOffset += items.length;
344
+ displayFunction(this.allItems, this.allItems.length, totalAvailable);
345
+ if (!this.showPrompt) {
346
+ continue;
347
+ }
348
+ const totalInfo = totalAvailable ? ` of ${totalAvailable.toLocaleString()}` : "";
349
+ const message = `Load more ${this.itemName}? (${this.allItems.length}${totalInfo} shown so far)`;
350
+ const { loadMore } = await inquirer2.prompt([
351
+ {
352
+ type: "confirm",
353
+ name: "loadMore",
354
+ message,
355
+ default: true
356
+ }
357
+ ]);
358
+ if (!loadMore) {
359
+ hasMore = false;
360
+ }
361
+ } catch (error) {
362
+ throw error;
363
+ }
364
+ }
365
+ return {
366
+ items: this.allItems,
367
+ hasMore: this.currentOffset > 0 && hasMore,
368
+ totalShown: this.allItems.length
369
+ };
370
+ }
371
+ /**
372
+ * Reset the pager state
373
+ */
374
+ reset() {
375
+ this.allItems = [];
376
+ this.currentOffset = 0;
377
+ }
378
+ };
379
+ function createPager(options = {}) {
380
+ return new Pager(options);
381
+ }
382
+
383
+ // src/utils/schema-formatter.ts
384
+ import chalk3 from "chalk";
385
+ function getFormatMetadata(schema) {
386
+ return schema?._def?.formatMeta;
387
+ }
388
+ function getOutputSchema(schema) {
389
+ return schema?._def?.outputSchema;
390
+ }
391
+ function formatItemsFromSchema(inputSchema, items) {
392
+ const outputSchema = getOutputSchema(inputSchema);
393
+ if (!outputSchema) {
394
+ formatItemsGeneric(items);
395
+ return;
396
+ }
397
+ const formatMeta = getFormatMetadata(outputSchema);
398
+ if (!formatMeta) {
399
+ formatItemsGeneric(items);
400
+ return;
401
+ }
402
+ items.forEach((item, index) => {
403
+ formatSingleItem(item, index, formatMeta);
404
+ });
405
+ }
406
+ function formatSingleItem(item, index, formatMeta) {
407
+ const formatted = formatMeta.format(item);
408
+ let titleLine = `${chalk3.gray(`${index + 1}.`)} ${chalk3.cyan(formatted.title)}`;
409
+ if (formatted.subtitle) {
410
+ titleLine += ` ${chalk3.gray(formatted.subtitle)}`;
411
+ }
412
+ console.log(titleLine);
413
+ for (const detail of formatted.details) {
414
+ const styledText = applyStyle(detail.text, detail.style);
415
+ console.log(` ${styledText}`);
416
+ }
417
+ console.log();
418
+ }
419
+ function applyStyle(value, style) {
420
+ switch (style) {
421
+ case "dim":
422
+ return chalk3.dim(value);
423
+ case "accent":
424
+ return chalk3.magenta(value);
425
+ case "warning":
426
+ return chalk3.red(value);
427
+ case "success":
428
+ return chalk3.green(value);
429
+ case "normal":
430
+ default:
431
+ return chalk3.blue(value);
432
+ }
433
+ }
434
+ function formatItemsGeneric(items) {
435
+ items.forEach((item, index) => {
436
+ const name = item.name || item.key || item.id || "Item";
437
+ console.log(`${chalk3.gray(`${index + 1}.`)} ${chalk3.cyan(name)}`);
438
+ if (item.description) {
439
+ console.log(` ${chalk3.dim(item.description)}`);
440
+ }
441
+ console.log();
442
+ });
443
+ }
444
+
445
+ // src/utils/cli-generator.ts
446
+ import chalk4 from "chalk";
447
+ import util from "util";
448
+ function formatJsonOutput(data) {
449
+ if (data && typeof data === "object" && !Array.isArray(data) && (data.success !== void 0 || data.id || data.status)) {
450
+ console.log(chalk4.green("\u2705 Action completed successfully!\n"));
451
+ }
452
+ console.log(
453
+ util.inspect(data, { colors: true, depth: null, breakLength: 80 })
454
+ );
455
+ }
456
+ function analyzeZodSchema(schema) {
457
+ const parameters = [];
458
+ if (schema instanceof z2.ZodObject) {
459
+ const shape = schema.shape;
460
+ for (const [key, fieldSchema] of Object.entries(shape)) {
461
+ const param = analyzeZodField(key, fieldSchema);
462
+ if (param) {
463
+ parameters.push(param);
464
+ }
465
+ }
466
+ }
467
+ return parameters;
468
+ }
469
+ function analyzeZodField(name, schema) {
470
+ let baseSchema = schema;
471
+ let required = true;
472
+ let defaultValue = void 0;
473
+ if (baseSchema instanceof z2.ZodOptional) {
474
+ required = false;
475
+ baseSchema = baseSchema._def.innerType;
476
+ }
477
+ if (baseSchema instanceof z2.ZodDefault) {
478
+ required = false;
479
+ defaultValue = baseSchema._def.defaultValue();
480
+ baseSchema = baseSchema._def.innerType;
481
+ }
482
+ let paramType = "string";
483
+ let choices;
484
+ if (baseSchema instanceof z2.ZodString) {
485
+ paramType = "string";
486
+ } else if (baseSchema instanceof z2.ZodNumber) {
487
+ paramType = "number";
488
+ } else if (baseSchema instanceof z2.ZodBoolean) {
489
+ paramType = "boolean";
490
+ } else if (baseSchema instanceof z2.ZodArray) {
491
+ paramType = "array";
492
+ } else if (baseSchema instanceof z2.ZodEnum) {
493
+ paramType = "string";
494
+ choices = baseSchema._def.values;
495
+ } else if (baseSchema instanceof z2.ZodRecord) {
496
+ paramType = "string";
497
+ }
498
+ return {
499
+ name,
500
+ type: paramType,
501
+ required,
502
+ description: schema.description,
503
+ default: defaultValue,
504
+ choices,
505
+ hasResolver: hasResolver2(name),
506
+ isPositional: isPositional(schema)
507
+ };
508
+ }
509
+ function toKebabCase(str) {
510
+ return str.replace(/([A-Z])/g, "-$1").toLowerCase();
511
+ }
512
+ function methodNameToCliCommand(methodName) {
513
+ return toKebabCase(methodName);
514
+ }
515
+ function generateCliCommands(program2, sdk2) {
516
+ if (!sdk2.__registry) {
517
+ console.error("SDK registry not available");
518
+ return;
519
+ }
520
+ sdk2.__registry.forEach((fnInfo) => {
521
+ if (!fnInfo.inputSchema) {
522
+ console.warn(`Schema not found for ${fnInfo.name}`);
523
+ return;
524
+ }
525
+ const cliCommandName = methodNameToCliCommand(fnInfo.name);
526
+ const config = createCommandConfig(
527
+ cliCommandName,
528
+ fnInfo.name,
529
+ fnInfo.inputSchema,
530
+ sdk2
531
+ );
532
+ addCommand(program2, cliCommandName, config);
533
+ });
534
+ }
535
+ function createCommandConfig(cliCommandName, sdkMethodName, schema, sdk2) {
536
+ const parameters = analyzeZodSchema(schema);
537
+ const description = schema.description || `${cliCommandName} command`;
538
+ const handler = async (...args) => {
539
+ try {
540
+ const commandObj = args[args.length - 1];
541
+ const options = commandObj.opts();
542
+ const isListCommand = cliCommandName.startsWith("list-");
543
+ const hasPaginationParams = parameters.some(
544
+ (p) => p.name === "limit" || p.name === "offset"
545
+ );
546
+ const hasUserSpecifiedLimit = "limit" in options && options.limit !== void 0;
547
+ const shouldUsePaging = isListCommand && hasPaginationParams && !hasUserSpecifiedLimit;
548
+ const shouldUseJson = options.json;
549
+ const rawParams = convertCliArgsToSdkParams(
550
+ parameters,
551
+ args.slice(0, -1),
552
+ options
553
+ );
554
+ const resolver = new SchemaParameterResolver();
555
+ const resolvedParams = await resolver.resolveParameters(
556
+ schema,
557
+ rawParams,
558
+ sdk2
559
+ );
560
+ if (shouldUsePaging && !shouldUseJson) {
561
+ await handlePaginatedList(sdkMethodName, resolvedParams, sdk2, schema);
562
+ } else {
563
+ const result = await sdk2[sdkMethodName](resolvedParams);
564
+ const hasOutputFile = resolvedParams.output;
565
+ if (!hasOutputFile && (shouldUseJson || !isListCommand)) {
566
+ if (shouldUseJson) {
567
+ console.log(JSON.stringify(result, null, 2));
568
+ } else {
569
+ formatJsonOutput(result);
570
+ }
571
+ } else if (!hasOutputFile) {
572
+ formatNonPaginatedResults(
573
+ result,
574
+ resolvedParams.limit,
575
+ hasUserSpecifiedLimit,
576
+ shouldUseJson,
577
+ schema,
578
+ sdkMethodName
579
+ );
580
+ } else if (hasOutputFile) {
581
+ console.log(
582
+ chalk4.green(`\u2705 ${cliCommandName} completed successfully!`)
583
+ );
584
+ console.log(
585
+ chalk4.gray(`Output written to: ${resolvedParams.output}`)
586
+ );
587
+ }
588
+ }
589
+ } catch (error) {
590
+ if (error instanceof Error && error.message.includes('"code"')) {
591
+ try {
592
+ const validationErrors = JSON.parse(error.message);
593
+ console.error(chalk4.red("\u274C Validation Error:"));
594
+ validationErrors.forEach((err) => {
595
+ const field = err.path?.join(".") || "unknown";
596
+ console.error(chalk4.yellow(` \u2022 ${field}: ${err.message}`));
597
+ });
598
+ console.error(
599
+ "\n" + chalk4.dim(`Use --help to see available options`)
600
+ );
601
+ } catch {
602
+ console.error(chalk4.red("Error:"), error.message);
603
+ }
604
+ } else {
605
+ console.error(
606
+ chalk4.red("Error:"),
607
+ error instanceof Error ? error.message : "Unknown error"
608
+ );
609
+ }
610
+ process.exit(1);
611
+ }
612
+ };
613
+ return {
614
+ description,
615
+ parameters,
616
+ handler
617
+ };
618
+ }
619
+ function addCommand(program2, commandName, config) {
620
+ const command = program2.command(commandName).description(config.description);
621
+ config.parameters.forEach((param) => {
622
+ const kebabName = param.name.replace(/([A-Z])/g, "-$1").toLowerCase();
623
+ if (param.hasResolver && param.required) {
624
+ command.argument(
625
+ `[${kebabName}]`,
626
+ param.description || `${kebabName} parameter`
627
+ );
628
+ } else if (param.required) {
629
+ command.argument(
630
+ `<${kebabName}>`,
631
+ param.description || `${kebabName} parameter`
632
+ );
633
+ } else if (param.isPositional) {
634
+ command.argument(
635
+ `[${kebabName}]`,
636
+ param.description || `${kebabName} parameter`
637
+ );
638
+ } else {
639
+ const flags = [`--${kebabName}`];
640
+ if (param.type === "boolean") {
641
+ command.option(flags.join(", "), param.description);
642
+ } else {
643
+ const flagSignature = flags.join(", ") + ` <${param.type}>`;
644
+ command.option(flagSignature, param.description, param.default);
645
+ }
646
+ }
647
+ });
648
+ command.option("--json", "Output raw JSON instead of formatted results");
649
+ command.action(config.handler);
650
+ }
651
+ function convertCliArgsToSdkParams(parameters, positionalArgs, options) {
652
+ const sdkParams = {};
653
+ let argIndex = 0;
654
+ parameters.forEach((param) => {
655
+ if ((param.required || param.isPositional) && argIndex < positionalArgs.length) {
656
+ sdkParams[param.name] = convertValue(
657
+ positionalArgs[argIndex],
658
+ param.type
659
+ );
660
+ argIndex++;
661
+ }
662
+ });
663
+ Object.entries(options).forEach(([key, value]) => {
664
+ const camelKey = key.replace(/-([a-z])/g, (g) => g[1].toUpperCase());
665
+ const param = parameters.find((p) => p.name === camelKey);
666
+ if (param && value !== void 0) {
667
+ sdkParams[camelKey] = convertValue(value, param.type);
668
+ }
669
+ });
670
+ return sdkParams;
671
+ }
672
+ function convertValue(value, type) {
673
+ switch (type) {
674
+ case "number":
675
+ return Number(value);
676
+ case "boolean":
677
+ return Boolean(value);
678
+ case "array":
679
+ return Array.isArray(value) ? value : [value];
680
+ case "string":
681
+ default:
682
+ if (typeof value === "string" && (value.startsWith("{") || value.startsWith("["))) {
683
+ try {
684
+ return JSON.parse(value);
685
+ } catch {
686
+ return value;
687
+ }
688
+ }
689
+ return value;
690
+ }
691
+ }
692
+ async function handlePaginatedList(sdkMethodName, baseParams, sdk2, schema) {
693
+ const limit = baseParams.limit || 20;
694
+ const itemName = getItemNameFromMethod(sdkMethodName);
695
+ console.log(chalk4.blue(`\u{1F4CB} Fetching ${itemName}...`));
696
+ const pager = createPager({
697
+ pageSize: Math.min(limit, 20),
698
+ itemName
699
+ });
700
+ const displayFunction = (items, totalShown, totalAvailable) => {
701
+ if (items.length > 0) {
702
+ console.clear();
703
+ }
704
+ console.log(chalk4.blue(`\u{1F4CB} ${getListTitleFromMethod(sdkMethodName)}
705
+ `));
706
+ if (items.length === 0) {
707
+ console.log(chalk4.yellow(`No ${itemName} found.`));
708
+ return;
709
+ }
710
+ if (schema) {
711
+ formatItemsFromSchema(schema, items);
712
+ } else {
713
+ formatItemsGeneric2(items);
714
+ }
715
+ const totalInfo = totalAvailable ? ` of ${totalAvailable.toLocaleString()} total` : "";
716
+ console.log(
717
+ chalk4.green(`
718
+ \u2705 Showing ${totalShown}${totalInfo} ${itemName}`)
719
+ );
720
+ };
721
+ await pager.paginate(
722
+ (params) => sdk2[sdkMethodName]({
723
+ ...baseParams,
724
+ ...params
725
+ }),
726
+ {},
727
+ displayFunction
728
+ );
729
+ }
730
+ function formatNonPaginatedResults(result, requestedLimit, userSpecifiedLimit, useRawJson, schema, methodName) {
731
+ if (!Array.isArray(result)) {
732
+ if (useRawJson) {
733
+ console.log(JSON.stringify(result, null, 2));
734
+ } else {
735
+ formatJsonOutput(result);
736
+ }
737
+ return;
738
+ }
739
+ if (useRawJson) {
740
+ console.log(JSON.stringify(result, null, 2));
741
+ return;
742
+ }
743
+ const itemName = methodName ? getItemNameFromMethod(methodName) : "items";
744
+ if (result.length === 0) {
745
+ console.log(chalk4.yellow(`No ${itemName} found.`));
746
+ return;
747
+ }
748
+ console.log(chalk4.green(`
749
+ \u2705 Found ${result.length} ${itemName}:
750
+ `));
751
+ if (schema) {
752
+ formatItemsFromSchema(schema, result);
753
+ } else {
754
+ formatItemsGeneric2(result);
755
+ }
756
+ if (userSpecifiedLimit && requestedLimit) {
757
+ console.log(
758
+ chalk4.gray(
759
+ `
760
+ \u{1F4C4} Showing up to ${requestedLimit} ${itemName} (--limit ${requestedLimit})`
761
+ )
762
+ );
763
+ } else {
764
+ console.log(chalk4.gray(`
765
+ \u{1F4C4} All available ${itemName} shown`));
766
+ }
767
+ }
768
+ function formatItemsGeneric2(items) {
769
+ items.forEach((item, index) => {
770
+ const name = item.name || item.key || item.id || "Item";
771
+ console.log(`${chalk4.gray(`${index + 1}.`)} ${chalk4.cyan(name)}`);
772
+ if (item.description) {
773
+ console.log(` ${chalk4.dim(item.description)}`);
774
+ }
775
+ console.log();
776
+ });
777
+ }
778
+ function getItemNameFromMethod(methodName) {
779
+ const listMatch = methodName.match(/^list(.+)$/);
780
+ if (listMatch) {
781
+ return listMatch[1].toLowerCase();
782
+ }
783
+ return "items";
784
+ }
785
+ function getListTitleFromMethod(methodName) {
786
+ const itemName = getItemNameFromMethod(methodName);
787
+ if (itemName === "items") return "Available Items";
788
+ const capitalized = itemName.charAt(0).toUpperCase() + itemName.slice(1);
789
+ return `Available ${capitalized}`;
790
+ }
791
+
792
+ // src/commands/login.ts
793
+ import { Command } from "commander";
794
+
795
+ // src/utils/auth/login.ts
796
+ import open from "open";
797
+ import crypto from "crypto";
798
+ import express from "express";
799
+ import pkceChallenge from "pkce-challenge";
800
+
801
+ // src/utils/constants.ts
802
+ var ZAPIER_BASE = "https://zapier.com";
803
+ var LOGIN_CLIENT_ID = "K5eEnRE9TTmSFATdkkWhKF8NOKwoiOnYAyIqJjae";
804
+ var LOGIN_PORTS = [49505, 50575, 52804, 55981, 61010, 63851];
805
+ var LOGIN_TIMEOUT_MS = 3e5;
806
+ var AUTH_MODE_HEADER = "X-Auth";
807
+
808
+ // src/utils/spinner.ts
809
+ import ora from "ora";
810
+ var spinPromise = async (promise, text) => {
811
+ const spinner = ora(text).start();
812
+ try {
813
+ const result = await promise;
814
+ spinner.succeed();
815
+ return result;
816
+ } catch (error) {
817
+ spinner.fail();
818
+ throw error;
819
+ }
820
+ };
821
+
822
+ // src/utils/log.ts
823
+ import chalk5 from "chalk";
824
+ var log = {
825
+ info: (message, ...args) => {
826
+ console.log(chalk5.blue("\u2139"), message, ...args);
827
+ },
828
+ error: (message, ...args) => {
829
+ console.error(chalk5.red("\u2716"), message, ...args);
830
+ },
831
+ success: (message, ...args) => {
832
+ console.log(chalk5.green("\u2713"), message, ...args);
833
+ },
834
+ warn: (message, ...args) => {
835
+ console.log(chalk5.yellow("\u26A0"), message, ...args);
836
+ }
837
+ };
838
+ var log_default = log;
839
+
840
+ // src/utils/api/client.ts
841
+ var createApiClient = () => {
842
+ const post = async (url, data, options = {}) => {
843
+ const { headers = {} } = options;
844
+ const response = await fetch(url, {
845
+ method: "POST",
846
+ headers: {
847
+ "Content-Type": "application/x-www-form-urlencoded",
848
+ Connection: "close",
849
+ ...headers
850
+ },
851
+ body: new URLSearchParams(data)
852
+ });
853
+ if (!response.ok) {
854
+ throw new Error(`${response.status} ${response.statusText}`);
855
+ }
856
+ const responseData = await response.json();
857
+ return {
858
+ data: responseData,
859
+ status: response.status
860
+ };
861
+ };
862
+ return {
863
+ post
864
+ };
865
+ };
866
+ var api = createApiClient();
867
+ var client_default = api;
868
+
869
+ // src/utils/getCallablePromise.ts
870
+ var getCallablePromise = () => {
871
+ let resolve = () => {
872
+ };
873
+ let reject = () => {
874
+ };
875
+ const promise = new Promise((_resolve, _reject) => {
876
+ resolve = _resolve;
877
+ reject = _reject;
878
+ });
879
+ return {
880
+ promise,
881
+ resolve,
882
+ reject
883
+ };
884
+ };
885
+ var getCallablePromise_default = getCallablePromise;
886
+
887
+ // src/utils/auth/login.ts
888
+ import { updateLogin, logout } from "@zapier/zapier-sdk-cli-login";
889
+ var findAvailablePort = () => {
890
+ return new Promise((resolve, reject) => {
891
+ let portIndex = 0;
892
+ const tryPort = (port) => {
893
+ const server = express().listen(port, () => {
894
+ server.close();
895
+ resolve(port);
896
+ });
897
+ server.on("error", (err) => {
898
+ if (err.code === "EADDRINUSE") {
899
+ if (portIndex < LOGIN_PORTS.length) {
900
+ tryPort(LOGIN_PORTS[portIndex++]);
901
+ } else {
902
+ reject(
903
+ new Error(
904
+ `All configured OAuth callback ports are busy: ${LOGIN_PORTS.join(", ")}. Please try again later or close applications using these ports.`
905
+ )
906
+ );
907
+ }
908
+ } else {
909
+ reject(err);
910
+ }
911
+ });
912
+ };
913
+ if (LOGIN_PORTS.length > 0) {
914
+ tryPort(LOGIN_PORTS[portIndex++]);
915
+ } else {
916
+ reject(new Error("No OAuth callback ports configured"));
917
+ }
918
+ });
919
+ };
920
+ var generateRandomString = () => {
921
+ const array = new Uint32Array(28);
922
+ crypto.getRandomValues(array);
923
+ return Array.from(
924
+ array,
925
+ (dec) => ("0" + dec.toString(16)).substring(-2)
926
+ ).join("");
927
+ };
928
+ var login = async (timeoutMs = LOGIN_TIMEOUT_MS) => {
929
+ logout();
930
+ const availablePort = await findAvailablePort();
931
+ const redirectUri = `http://localhost:${availablePort}/oauth`;
932
+ log_default.info(`Using port ${availablePort} for OAuth callback`);
933
+ const { promise: promisedCode, resolve: setCode } = getCallablePromise_default();
934
+ const app = express();
935
+ app.get("/oauth", (req, res) => {
936
+ setCode(String(req.query.code));
937
+ res.setHeader("Connection", "close");
938
+ res.end("You can now close this tab and return to the CLI.");
939
+ });
940
+ const server = app.listen(availablePort);
941
+ const connections = /* @__PURE__ */ new Set();
942
+ server.on("connection", (conn) => {
943
+ connections.add(conn);
944
+ conn.on("close", () => connections.delete(conn));
945
+ });
946
+ const cleanup = () => {
947
+ server.close();
948
+ log_default.info("\n\u274C Login cancelled by user");
949
+ process.exit(0);
950
+ };
951
+ process.on("SIGINT", cleanup);
952
+ process.on("SIGTERM", cleanup);
953
+ const { code_verifier: codeVerifier, code_challenge: codeChallenge } = await pkceChallenge();
954
+ const authUrl = `${ZAPIER_BASE}/oauth/authorize/?${new URLSearchParams({
955
+ response_type: "code",
956
+ client_id: LOGIN_CLIENT_ID,
957
+ redirect_uri: redirectUri,
958
+ scope: "internal offline_access",
959
+ state: generateRandomString(),
960
+ code_challenge: codeChallenge,
961
+ code_challenge_method: "S256"
962
+ }).toString()}`;
963
+ log_default.info("Opening your browser to log in.");
964
+ log_default.info("If it doesn't open, visit:", authUrl);
965
+ open(authUrl);
966
+ try {
967
+ await spinPromise(
968
+ Promise.race([
969
+ promisedCode,
970
+ new Promise(
971
+ (_resolve, reject) => setTimeout(() => {
972
+ reject(
973
+ new Error(
974
+ `Login timed out after ${Math.round(timeoutMs / 1e3)} seconds.`
975
+ )
976
+ );
977
+ }, timeoutMs)
978
+ )
979
+ ]),
980
+ "Waiting for you to login and authorize"
981
+ );
982
+ } finally {
983
+ process.off("SIGINT", cleanup);
984
+ process.off("SIGTERM", cleanup);
985
+ await new Promise((resolve) => {
986
+ const timeout = setTimeout(() => {
987
+ log_default.info("Server close timed out, forcing connection shutdown...");
988
+ connections.forEach((conn) => conn.destroy());
989
+ resolve();
990
+ }, 1e3);
991
+ server.close(() => {
992
+ clearTimeout(timeout);
993
+ resolve();
994
+ });
995
+ });
996
+ }
997
+ log_default.info("Exchanging authorization code for tokens...");
998
+ const { data } = await client_default.post(
999
+ `${ZAPIER_BASE}/oauth/token/`,
1000
+ {
1001
+ grant_type: "authorization_code",
1002
+ code: await promisedCode,
1003
+ redirect_uri: redirectUri,
1004
+ client_id: LOGIN_CLIENT_ID,
1005
+ code_verifier: codeVerifier
1006
+ },
1007
+ {
1008
+ headers: {
1009
+ [AUTH_MODE_HEADER]: "no",
1010
+ "Content-Type": "application/x-www-form-urlencoded"
1011
+ }
1012
+ }
1013
+ );
1014
+ updateLogin(data);
1015
+ log_default.info("Token exchange completed successfully");
1016
+ return data.access_token;
1017
+ };
1018
+ var login_default = login;
1019
+
1020
+ // src/commands/login.ts
1021
+ import { getLoggedInUser } from "@zapier/zapier-sdk-cli-login";
1022
+ function createLoginCommand() {
1023
+ return new Command("login").description("Log in to Zapier to access your account").option(
1024
+ "--timeout <seconds>",
1025
+ "Login timeout in seconds (default: 300)",
1026
+ "300"
1027
+ ).action(async (options) => {
1028
+ try {
1029
+ const timeoutSeconds = parseInt(options.timeout, 10);
1030
+ if (isNaN(timeoutSeconds) || timeoutSeconds <= 0) {
1031
+ throw new Error("Timeout must be a positive number");
1032
+ }
1033
+ await login_default(timeoutSeconds * 1e3);
1034
+ const user = await getLoggedInUser();
1035
+ console.log(`\u2705 Successfully logged in as ${user.email}`);
1036
+ setTimeout(() => process.exit(0), 100);
1037
+ } catch (error) {
1038
+ console.error(
1039
+ "\u274C Login failed:",
1040
+ error instanceof Error ? error.message : "Unknown error"
1041
+ );
1042
+ process.exit(1);
1043
+ }
1044
+ });
1045
+ }
1046
+
1047
+ // src/commands/logout.ts
1048
+ import { Command as Command2 } from "commander";
1049
+ import { logout as logout2 } from "@zapier/zapier-sdk-cli-login";
1050
+ function createLogoutCommand() {
1051
+ return new Command2("logout").description("Log out of your Zapier account").action(async () => {
1052
+ try {
1053
+ logout2();
1054
+ console.log("\u2705 Successfully logged out");
1055
+ } catch (error) {
1056
+ console.error(
1057
+ "\u274C Logout failed:",
1058
+ error instanceof Error ? error.message : "Unknown error"
1059
+ );
1060
+ process.exit(1);
1061
+ }
1062
+ });
1063
+ }
1064
+
1065
+ // src/commands/whoami.ts
1066
+ import { Command as Command3 } from "commander";
1067
+ import { getLoggedInUser as getLoggedInUser2 } from "@zapier/zapier-sdk-cli-login";
1068
+ function createWhoamiCommand() {
1069
+ return new Command3("whoami").description("Show current login status and user information").action(async () => {
1070
+ try {
1071
+ const user = await spinPromise(
1072
+ getLoggedInUser2(),
1073
+ "Checking login status..."
1074
+ );
1075
+ console.log(
1076
+ `\u2705 Logged in as ${user.email} (Account ID: ${user.accountId})`
1077
+ );
1078
+ } catch {
1079
+ console.log(
1080
+ "\u274C Not logged in. Use 'zapier-sdk login' to authenticate."
1081
+ );
1082
+ process.exit(1);
1083
+ }
1084
+ });
1085
+ }
1086
+
1087
+ // src/commands/configPath.ts
1088
+ import { Command as Command4 } from "commander";
1089
+ import { getConfigPath } from "@zapier/zapier-sdk-cli-login";
1090
+ function createConfigPathCommand() {
1091
+ return new Command4("get-config-path").description("Show the path to the configuration file").action(async () => {
1092
+ console.log(`Configuration file: ${getConfigPath()}`);
1093
+ });
1094
+ }
1095
+
1096
+ // src/cli.ts
1097
+ var program = new Command5();
1098
+ program.name("zapier-sdk").description("CLI for Zapier SDK - Commands auto-generated from SDK schemas").version("1.0.0").option("--debug", "Enable debug logging");
1099
+ var isDebugMode = process.env.DEBUG === "true" || process.argv.includes("--debug");
1100
+ var sdk = createZapierSdk({
1101
+ debug: isDebugMode
17
1102
  });
18
- // Add auth commands before generating SDK commands
19
1103
  program.addCommand(createLoginCommand());
20
1104
  program.addCommand(createLogoutCommand());
21
1105
  program.addCommand(createWhoamiCommand());
22
1106
  program.addCommand(createConfigPathCommand());
23
- // Generate CLI commands from SDK schemas
24
1107
  generateCliCommands(program, sdk);
25
1108
  program.parse();