@zapier/zapier-sdk-cli 0.1.1 → 0.2.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.
Files changed (44) hide show
  1. package/dist/cli.js +13 -11
  2. package/dist/commands/configPath.d.ts +2 -0
  3. package/dist/commands/configPath.js +9 -0
  4. package/dist/commands/index.d.ts +4 -0
  5. package/dist/commands/index.js +4 -0
  6. package/dist/commands/login.d.ts +2 -0
  7. package/dist/commands/login.js +25 -0
  8. package/dist/commands/logout.d.ts +2 -0
  9. package/dist/commands/logout.js +16 -0
  10. package/dist/commands/whoami.d.ts +2 -0
  11. package/dist/commands/whoami.js +17 -0
  12. package/dist/utils/api/client.d.ts +15 -0
  13. package/dist/utils/api/client.js +27 -0
  14. package/dist/utils/auth/login.d.ts +2 -0
  15. package/dist/utils/auth/login.js +134 -0
  16. package/dist/utils/cli-generator.js +42 -48
  17. package/dist/utils/constants.d.ts +5 -0
  18. package/dist/utils/constants.js +6 -0
  19. package/dist/utils/getCallablePromise.d.ts +6 -0
  20. package/dist/utils/getCallablePromise.js +14 -0
  21. package/dist/utils/log.d.ts +7 -0
  22. package/dist/utils/log.js +16 -0
  23. package/dist/utils/pager.js +10 -20
  24. package/dist/utils/parameter-resolver.js +34 -41
  25. package/dist/utils/schema-formatter.js +11 -17
  26. package/dist/utils/serializeAsync.d.ts +2 -0
  27. package/dist/utils/serializeAsync.js +16 -0
  28. package/dist/utils/spinner.d.ts +1 -0
  29. package/dist/utils/spinner.js +13 -0
  30. package/package.json +8 -2
  31. package/src/cli.ts +13 -3
  32. package/src/commands/configPath.ts +10 -0
  33. package/src/commands/index.ts +4 -0
  34. package/src/commands/login.ts +34 -0
  35. package/src/commands/logout.ts +19 -0
  36. package/src/commands/whoami.ts +25 -0
  37. package/src/utils/api/client.ts +44 -0
  38. package/src/utils/auth/login.ts +190 -0
  39. package/src/utils/constants.ts +9 -0
  40. package/src/utils/getCallablePromise.ts +21 -0
  41. package/src/utils/log.ts +18 -0
  42. package/src/utils/serializeAsync.ts +26 -0
  43. package/src/utils/spinner.ts +16 -0
  44. package/tsconfig.json +2 -2
@@ -1,19 +1,10 @@
1
- "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
5
- Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.Pager = void 0;
7
- exports.createPager = createPager;
8
- exports.fetchAllPages = fetchAllPages;
9
- exports.showPaginationStatus = showPaginationStatus;
10
- const inquirer_1 = __importDefault(require("inquirer"));
11
- const chalk_1 = __importDefault(require("chalk"));
1
+ import inquirer from "inquirer";
2
+ import chalk from "chalk";
12
3
  /**
13
4
  * Generic pager that handles pagination for any data source
14
5
  * Supports interactive "load more" functionality
15
6
  */
16
- class Pager {
7
+ export class Pager {
17
8
  constructor(options = {}) {
18
9
  this.allItems = [];
19
10
  this.currentOffset = 0;
@@ -67,7 +58,7 @@ class Pager {
67
58
  : "";
68
59
  const message = `Load more ${this.itemName}? (${this.allItems.length}${totalInfo} shown so far)`;
69
60
  // Ask user if they want to load more
70
- const { loadMore } = await inquirer_1.default.prompt([
61
+ const { loadMore } = await inquirer.prompt([
71
62
  {
72
63
  type: "confirm",
73
64
  name: "loadMore",
@@ -98,17 +89,16 @@ class Pager {
98
89
  this.currentOffset = 0;
99
90
  }
100
91
  }
101
- exports.Pager = Pager;
102
92
  /**
103
93
  * Convenience function to create a pager for a specific use case
104
94
  */
105
- function createPager(options = {}) {
95
+ export function createPager(options = {}) {
106
96
  return new Pager(options);
107
97
  }
108
98
  /**
109
99
  * Simple pagination without interactivity - just fetches all results up to a limit
110
100
  */
111
- async function fetchAllPages(fetchFunction, baseParams, maxResults = 100) {
101
+ export async function fetchAllPages(fetchFunction, baseParams, maxResults = 100) {
112
102
  const pageSize = Math.min(maxResults, 50); // Reasonable page size
113
103
  const allItems = [];
114
104
  let offset = 0;
@@ -136,12 +126,12 @@ async function fetchAllPages(fetchFunction, baseParams, maxResults = 100) {
136
126
  /**
137
127
  * Display helper for showing pagination status
138
128
  */
139
- function showPaginationStatus(currentCount, requestedLimit, itemName = "items") {
129
+ export function showPaginationStatus(currentCount, requestedLimit, itemName = "items") {
140
130
  if (currentCount >= requestedLimit) {
141
- console.log(chalk_1.default.yellow(`\nšŸ“„ Showing first ${currentCount} ${itemName} (limit: ${requestedLimit})`));
142
- console.log(chalk_1.default.gray(`Use --limit to increase the limit, or add paging with --page`));
131
+ console.log(chalk.yellow(`\nšŸ“„ Showing first ${currentCount} ${itemName} (limit: ${requestedLimit})`));
132
+ console.log(chalk.gray(`Use --limit to increase the limit, or add paging with --page`));
143
133
  }
144
134
  else {
145
- console.log(chalk_1.default.gray(`\nšŸ“„ Showing all ${currentCount} ${itemName}`));
135
+ console.log(chalk.gray(`\nšŸ“„ Showing all ${currentCount} ${itemName}`));
146
136
  }
147
137
  }
@@ -1,23 +1,17 @@
1
- "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
5
- Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.SchemaParameterResolver = void 0;
7
- const inquirer_1 = __importDefault(require("inquirer"));
8
- const chalk_1 = __importDefault(require("chalk"));
9
- const zod_1 = require("zod");
10
- const zapier_sdk_1 = require("@zapier/zapier-sdk");
1
+ import inquirer from "inquirer";
2
+ import chalk from "chalk";
3
+ import { z } from "zod";
4
+ import { getResolver, hasResolver, getResolutionOrderForParams, } from "@zapier/zapier-sdk";
11
5
  // ============================================================================
12
6
  // Schema Parameter Resolver
13
7
  // ============================================================================
14
- class SchemaParameterResolver {
8
+ export class SchemaParameterResolver {
15
9
  async resolveParameters(schema, providedParams, sdk) {
16
10
  // 1. Try to parse with current parameters
17
11
  const parseResult = schema.safeParse(providedParams);
18
12
  // Get all schema parameters to check which ones have resolvers
19
13
  const allParams = this.extractParametersFromSchema(schema);
20
- const resolvableParams = allParams.filter((param) => (0, zapier_sdk_1.hasResolver)(param.name));
14
+ const resolvableParams = allParams.filter((param) => hasResolver(param.name));
21
15
  // Get all missing parameters that have resolvers
22
16
  const missingResolvable = resolvableParams.filter((param) => {
23
17
  const hasValue = this.getNestedValue(providedParams, param.path) !== undefined;
@@ -57,7 +51,7 @@ class SchemaParameterResolver {
57
51
  };
58
52
  if (functionallyRequired.length > 0) {
59
53
  const requiredParamNames = functionallyRequired.map((p) => p.name);
60
- const requiredResolutionOrder = (0, zapier_sdk_1.getResolutionOrderForParams)(requiredParamNames);
54
+ const requiredResolutionOrder = getResolutionOrderForParams(requiredParamNames);
61
55
  const orderedRequiredParams = requiredResolutionOrder
62
56
  .map((paramName) => functionallyRequired.find((p) => p.name === paramName))
63
57
  .filter((param) => param !== undefined);
@@ -70,10 +64,10 @@ class SchemaParameterResolver {
70
64
  }
71
65
  catch (error) {
72
66
  if (this.isUserCancellation(error)) {
73
- console.log(chalk_1.default.yellow("\n\nOperation cancelled by user"));
67
+ console.log(chalk.yellow("\n\nOperation cancelled by user"));
74
68
  process.exit(0);
75
69
  }
76
- console.error(chalk_1.default.red(`Failed to resolve ${param.name}:`), error);
70
+ console.error(chalk.red(`Failed to resolve ${param.name}:`), error);
77
71
  throw error;
78
72
  }
79
73
  }
@@ -81,7 +75,7 @@ class SchemaParameterResolver {
81
75
  // 3. Ask user if they want to resolve truly optional parameters
82
76
  if (trulyOptional.length > 0) {
83
77
  const optionalNames = trulyOptional.map((p) => p.name).join(", ");
84
- const shouldResolveOptional = await inquirer_1.default.prompt([
78
+ const shouldResolveOptional = await inquirer.prompt([
85
79
  {
86
80
  type: "confirm",
87
81
  name: "resolveOptional",
@@ -92,7 +86,7 @@ class SchemaParameterResolver {
92
86
  if (shouldResolveOptional.resolveOptional) {
93
87
  // Resolve optional parameters using their resolvers
94
88
  const optionalParamNames = trulyOptional.map((p) => p.name);
95
- const optionalResolutionOrder = (0, zapier_sdk_1.getResolutionOrderForParams)(optionalParamNames);
89
+ const optionalResolutionOrder = getResolutionOrderForParams(optionalParamNames);
96
90
  const orderedOptionalParams = optionalResolutionOrder
97
91
  .map((paramName) => trulyOptional.find((p) => p.name === paramName))
98
92
  .filter((param) => param !== undefined);
@@ -105,10 +99,10 @@ class SchemaParameterResolver {
105
99
  }
106
100
  catch (error) {
107
101
  if (this.isUserCancellation(error)) {
108
- console.log(chalk_1.default.yellow("\n\nOperation cancelled by user"));
102
+ console.log(chalk.yellow("\n\nOperation cancelled by user"));
109
103
  process.exit(0);
110
104
  }
111
- console.error(chalk_1.default.red(`Failed to resolve ${param.name}:`), error);
105
+ console.error(chalk.red(`Failed to resolve ${param.name}:`), error);
112
106
  throw error;
113
107
  }
114
108
  }
@@ -117,7 +111,7 @@ class SchemaParameterResolver {
117
111
  // 3. Validate final parameters
118
112
  const finalResult = schema.safeParse(resolvedParams);
119
113
  if (!finalResult.success) {
120
- console.error(chalk_1.default.red("āŒ Parameter validation failed after resolution:"));
114
+ console.error(chalk.red("āŒ Parameter validation failed after resolution:"));
121
115
  throw finalResult.error;
122
116
  }
123
117
  return finalResult.data;
@@ -125,7 +119,7 @@ class SchemaParameterResolver {
125
119
  extractParametersFromSchema(schema) {
126
120
  const parameters = [];
127
121
  // Only handle ZodObject at the top level
128
- if (schema instanceof zod_1.z.ZodObject) {
122
+ if (schema instanceof z.ZodObject) {
129
123
  const shape = schema.shape;
130
124
  for (const [key, fieldSchema] of Object.entries(shape)) {
131
125
  const param = this.analyzeFieldSchema(key, fieldSchema);
@@ -140,11 +134,11 @@ class SchemaParameterResolver {
140
134
  let baseSchema = fieldSchema;
141
135
  let isRequired = true;
142
136
  // Check if field is optional or has default
143
- if (baseSchema instanceof zod_1.z.ZodOptional) {
137
+ if (baseSchema instanceof z.ZodOptional) {
144
138
  isRequired = false;
145
139
  baseSchema = baseSchema._def.innerType;
146
140
  }
147
- if (baseSchema instanceof zod_1.z.ZodDefault) {
141
+ if (baseSchema instanceof z.ZodDefault) {
148
142
  isRequired = false;
149
143
  baseSchema = baseSchema._def.innerType;
150
144
  }
@@ -163,11 +157,11 @@ class SchemaParameterResolver {
163
157
  };
164
158
  }
165
159
  async resolveParameter(param, context) {
166
- const resolver = (0, zapier_sdk_1.getResolver)(param.name);
160
+ const resolver = getResolver(param.name);
167
161
  if (!resolver) {
168
162
  throw new Error(`No resolver found for parameter: ${param.name}`);
169
163
  }
170
- console.log(chalk_1.default.blue(`\nšŸ” Resolving ${param.name}...`));
164
+ console.log(chalk.blue(`\nšŸ” Resolving ${param.name}...`));
171
165
  if (resolver.type === "static") {
172
166
  // Static resolver - just prompt for input
173
167
  const promptConfig = {
@@ -176,33 +170,33 @@ class SchemaParameterResolver {
176
170
  message: `Enter ${param.name}:`,
177
171
  ...(resolver.placeholder && { default: resolver.placeholder }),
178
172
  };
179
- const answers = await inquirer_1.default.prompt([promptConfig]);
173
+ const answers = await inquirer.prompt([promptConfig]);
180
174
  return answers[param.name];
181
175
  }
182
176
  else if (resolver.type === "dynamic") {
183
177
  // Dynamic resolver - fetch options and prompt for selection
184
178
  try {
185
- console.log(chalk_1.default.gray(`Fetching options for ${param.name}...`));
179
+ console.log(chalk.gray(`Fetching options for ${param.name}...`));
186
180
  const items = await resolver.fetch(context.sdk, context.resolvedParams);
187
181
  if (!items || items.length === 0) {
188
182
  throw new Error(`No options available for ${param.name}`);
189
183
  }
190
184
  const promptConfig = resolver.prompt(items, context.resolvedParams);
191
- const answers = await inquirer_1.default.prompt([promptConfig]);
185
+ const answers = await inquirer.prompt([promptConfig]);
192
186
  return answers[param.name];
193
187
  }
194
188
  catch (error) {
195
- console.error(chalk_1.default.red(`Failed to fetch options for ${param.name}:`), error instanceof Error ? error.message : error);
189
+ console.error(chalk.red(`Failed to fetch options for ${param.name}:`), error instanceof Error ? error.message : error);
196
190
  throw error;
197
191
  }
198
192
  }
199
193
  else if (resolver.type === "fields") {
200
194
  // Fields resolver - fetch field definitions and prompt for each input
201
195
  try {
202
- console.log(chalk_1.default.gray(`Fetching input fields for ${param.name}...`));
196
+ console.log(chalk.gray(`Fetching input fields for ${param.name}...`));
203
197
  const fields = await resolver.fetch(context.sdk, context.resolvedParams);
204
198
  if (!fields || fields.length === 0) {
205
- console.log(chalk_1.default.yellow(`No input fields required for this action.`));
199
+ console.log(chalk.yellow(`No input fields required for this action.`));
206
200
  return {};
207
201
  }
208
202
  const inputs = {};
@@ -211,17 +205,17 @@ class SchemaParameterResolver {
211
205
  const optionalFields = fields.filter((field) => !field.required);
212
206
  // First, prompt for all required fields
213
207
  if (requiredFields.length > 0) {
214
- console.log(chalk_1.default.blue(`\nšŸ“ Please provide values for the following input fields:`));
208
+ console.log(chalk.blue(`\nšŸ“ Please provide values for the following input fields:`));
215
209
  for (const field of requiredFields) {
216
210
  await this.promptForField(field, inputs);
217
211
  }
218
212
  }
219
213
  // Then ask if user wants to configure optional fields
220
214
  if (optionalFields.length > 0) {
221
- console.log(chalk_1.default.gray(`\nThere are ${optionalFields.length} optional field(s) available.`));
215
+ console.log(chalk.gray(`\nThere are ${optionalFields.length} optional field(s) available.`));
222
216
  let shouldConfigureOptional;
223
217
  try {
224
- shouldConfigureOptional = await inquirer_1.default.prompt([
218
+ shouldConfigureOptional = await inquirer.prompt([
225
219
  {
226
220
  type: "confirm",
227
221
  name: "configure",
@@ -232,13 +226,13 @@ class SchemaParameterResolver {
232
226
  }
233
227
  catch (error) {
234
228
  if (this.isUserCancellation(error)) {
235
- console.log(chalk_1.default.yellow("\n\nOperation cancelled by user"));
229
+ console.log(chalk.yellow("\n\nOperation cancelled by user"));
236
230
  process.exit(0);
237
231
  }
238
232
  throw error;
239
233
  }
240
234
  if (shouldConfigureOptional.configure) {
241
- console.log(chalk_1.default.cyan(`\nOptional fields:`));
235
+ console.log(chalk.cyan(`\nOptional fields:`));
242
236
  for (const field of optionalFields) {
243
237
  await this.promptForField(field, inputs);
244
238
  }
@@ -247,7 +241,7 @@ class SchemaParameterResolver {
247
241
  return inputs;
248
242
  }
249
243
  catch (error) {
250
- console.error(chalk_1.default.red(`Failed to fetch fields for ${param.name}:`), error instanceof Error ? error.message : error);
244
+ console.error(chalk.red(`Failed to fetch fields for ${param.name}:`), error instanceof Error ? error.message : error);
251
245
  throw error;
252
246
  }
253
247
  }
@@ -271,7 +265,7 @@ class SchemaParameterResolver {
271
265
  type: field.type === "boolean" ? "confirm" : "input",
272
266
  name: field.key,
273
267
  message: `${field.label || field.key}${field.required ? " (required)" : " (optional)"}:`,
274
- ...(field.helpText && { prefix: chalk_1.default.gray(`ℹ ${field.helpText}\n`) }),
268
+ ...(field.helpText && { prefix: chalk.gray(`ℹ ${field.helpText}\n`) }),
275
269
  ...(field.default && { default: field.default }),
276
270
  };
277
271
  if (field.choices && field.choices.length > 0) {
@@ -282,7 +276,7 @@ class SchemaParameterResolver {
282
276
  }));
283
277
  }
284
278
  try {
285
- const answer = await inquirer_1.default.prompt([fieldPrompt]);
279
+ const answer = await inquirer.prompt([fieldPrompt]);
286
280
  if (answer[field.key] !== undefined && answer[field.key] !== "") {
287
281
  inputs[field.key] = answer[field.key];
288
282
  }
@@ -292,7 +286,7 @@ class SchemaParameterResolver {
292
286
  }
293
287
  catch (error) {
294
288
  if (this.isUserCancellation(error)) {
295
- console.log(chalk_1.default.yellow("\n\nOperation cancelled by user"));
289
+ console.log(chalk.yellow("\n\nOperation cancelled by user"));
296
290
  process.exit(0);
297
291
  }
298
292
  throw error;
@@ -304,4 +298,3 @@ class SchemaParameterResolver {
304
298
  error?.isTTYError);
305
299
  }
306
300
  }
307
- exports.SchemaParameterResolver = SchemaParameterResolver;
@@ -1,10 +1,4 @@
1
- "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
5
- Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.formatItemsFromSchema = formatItemsFromSchema;
7
- const chalk_1 = __importDefault(require("chalk"));
1
+ import chalk from "chalk";
8
2
  function getFormatMetadata(schema) {
9
3
  return schema?._def?.formatMeta;
10
4
  }
@@ -14,7 +8,7 @@ function getOutputSchema(schema) {
14
8
  // ============================================================================
15
9
  // Generic Schema-Driven Formatter
16
10
  // ============================================================================
17
- function formatItemsFromSchema(inputSchema, items) {
11
+ export function formatItemsFromSchema(inputSchema, items) {
18
12
  // Get the output schema and its format metadata
19
13
  const outputSchema = getOutputSchema(inputSchema);
20
14
  if (!outputSchema) {
@@ -37,9 +31,9 @@ function formatSingleItem(item, index, formatMeta) {
37
31
  // Get the formatted item from the format function
38
32
  const formatted = formatMeta.format(item);
39
33
  // Build the main title line
40
- let titleLine = `${chalk_1.default.gray(`${index + 1}.`)} ${chalk_1.default.cyan(formatted.title)}`;
34
+ let titleLine = `${chalk.gray(`${index + 1}.`)} ${chalk.cyan(formatted.title)}`;
41
35
  if (formatted.subtitle) {
42
- titleLine += ` ${chalk_1.default.gray(formatted.subtitle)}`;
36
+ titleLine += ` ${chalk.gray(formatted.subtitle)}`;
43
37
  }
44
38
  console.log(titleLine);
45
39
  // Format detail lines
@@ -52,25 +46,25 @@ function formatSingleItem(item, index, formatMeta) {
52
46
  function applyStyle(value, style) {
53
47
  switch (style) {
54
48
  case "dim":
55
- return chalk_1.default.dim(value);
49
+ return chalk.dim(value);
56
50
  case "accent":
57
- return chalk_1.default.magenta(value);
51
+ return chalk.magenta(value);
58
52
  case "warning":
59
- return chalk_1.default.red(value);
53
+ return chalk.red(value);
60
54
  case "success":
61
- return chalk_1.default.green(value);
55
+ return chalk.green(value);
62
56
  case "normal":
63
57
  default:
64
- return chalk_1.default.blue(value);
58
+ return chalk.blue(value);
65
59
  }
66
60
  }
67
61
  function formatItemsGeneric(items) {
68
62
  // Fallback formatting for items without schema metadata
69
63
  items.forEach((item, index) => {
70
64
  const name = item.name || item.key || item.id || "Item";
71
- console.log(`${chalk_1.default.gray(`${index + 1}.`)} ${chalk_1.default.cyan(name)}`);
65
+ console.log(`${chalk.gray(`${index + 1}.`)} ${chalk.cyan(name)}`);
72
66
  if (item.description) {
73
- console.log(` ${chalk_1.default.dim(item.description)}`);
67
+ console.log(` ${chalk.dim(item.description)}`);
74
68
  }
75
69
  console.log();
76
70
  });
@@ -0,0 +1,2 @@
1
+ declare const serializeAsync: <R, A extends []>(fn: (...args: A) => Promise<R>) => ((...args: A) => Promise<R>);
2
+ export default serializeAsync;
@@ -0,0 +1,16 @@
1
+ import { createHash } from "node:crypto";
2
+ const promises = {};
3
+ const serializeAsync = (fn) => async (...args) => {
4
+ const hash = createHash("sha256")
5
+ .update(fn.toString(), "utf8")
6
+ .digest()
7
+ .toString("hex");
8
+ const promise = promises[hash];
9
+ if (!promise) {
10
+ promises[hash] = fn(...args).finally(() => {
11
+ delete promises[hash];
12
+ });
13
+ }
14
+ return promises[hash];
15
+ };
16
+ export default serializeAsync;
@@ -0,0 +1 @@
1
+ export declare const spinPromise: <T>(promise: Promise<T>, text: string) => Promise<T>;
@@ -0,0 +1,13 @@
1
+ import ora from "ora";
2
+ export const spinPromise = async (promise, text) => {
3
+ const spinner = ora(text).start();
4
+ try {
5
+ const result = await promise;
6
+ spinner.succeed();
7
+ return result;
8
+ }
9
+ catch (error) {
10
+ spinner.fail();
11
+ throw error;
12
+ }
13
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zapier/zapier-sdk-cli",
3
- "version": "0.1.1",
3
+ "version": "0.2.1",
4
4
  "description": "Command line interface for Zapier SDK",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -22,13 +22,19 @@
22
22
  "chalk": "^5.3.0",
23
23
  "cli-table3": "^0.6.5",
24
24
  "commander": "^12.0.0",
25
+ "express": "^5.1.0",
25
26
  "inquirer": "^12.6.3",
27
+ "jsonwebtoken": "^9.0.2",
28
+ "open": "^10.2.0",
26
29
  "ora": "^8.2.0",
30
+ "pkce-challenge": "^5.0.0",
27
31
  "zod": "^3.25.67",
28
- "@zapier/zapier-sdk": "0.1.1"
32
+ "@zapier/zapier-sdk": "0.2.1"
29
33
  },
30
34
  "devDependencies": {
35
+ "@types/express": "^5.0.3",
31
36
  "@types/inquirer": "^9.0.8",
37
+ "@types/jsonwebtoken": "^9.0.10",
32
38
  "@types/node": "^24.0.1",
33
39
  "typescript": "^5.8.3",
34
40
  "vitest": "^3.2.3"
package/src/cli.ts CHANGED
@@ -3,6 +3,12 @@
3
3
  import { Command } from "commander";
4
4
  import { createZapierSdk } from "@zapier/zapier-sdk";
5
5
  import { generateCliCommands } from "./utils/cli-generator";
6
+ import {
7
+ createLoginCommand,
8
+ createLogoutCommand,
9
+ createWhoamiCommand,
10
+ createConfigPathCommand,
11
+ } from "./commands";
6
12
 
7
13
  const program = new Command();
8
14
 
@@ -16,13 +22,17 @@ program
16
22
  const isDebugMode =
17
23
  process.env.DEBUG === "true" || process.argv.includes("--debug");
18
24
 
19
- // Create SDK instance for CLI operations
20
- // Auth will be resolved from environment variables or command options
25
+ // Create SDK instance - automatically handles token resolution
21
26
  const sdk = createZapierSdk({
22
- // Token will be picked up from ZAPIER_TOKEN env var or provided via options
23
27
  debug: isDebugMode,
24
28
  });
25
29
 
30
+ // Add auth commands before generating SDK commands
31
+ program.addCommand(createLoginCommand());
32
+ program.addCommand(createLogoutCommand());
33
+ program.addCommand(createWhoamiCommand());
34
+ program.addCommand(createConfigPathCommand());
35
+
26
36
  // Generate CLI commands from SDK schemas
27
37
  generateCliCommands(program, sdk);
28
38
 
@@ -0,0 +1,10 @@
1
+ import { Command } from "commander";
2
+ import { getConfigPath } from "@zapier/zapier-sdk";
3
+
4
+ export function createConfigPathCommand(): Command {
5
+ return new Command("get-config-path")
6
+ .description("Show the path to the configuration file")
7
+ .action(async () => {
8
+ console.log(`Configuration file: ${getConfigPath()}`);
9
+ });
10
+ }
@@ -0,0 +1,4 @@
1
+ export { createLoginCommand } from "./login";
2
+ export { createLogoutCommand } from "./logout";
3
+ export { createWhoamiCommand } from "./whoami";
4
+ export { createConfigPathCommand } from "./configPath";
@@ -0,0 +1,34 @@
1
+ import { Command } from "commander";
2
+ import login from "../utils/auth/login";
3
+ import { getLoggedInUser } from "@zapier/zapier-sdk";
4
+
5
+ export function createLoginCommand(): Command {
6
+ return new Command("login")
7
+ .description("Log in to Zapier to access your account")
8
+ .option(
9
+ "--timeout <seconds>",
10
+ "Login timeout in seconds (default: 300)",
11
+ "300",
12
+ )
13
+ .action(async (options) => {
14
+ try {
15
+ const timeoutSeconds = parseInt(options.timeout, 10);
16
+ if (isNaN(timeoutSeconds) || timeoutSeconds <= 0) {
17
+ throw new Error("Timeout must be a positive number");
18
+ }
19
+ await login(timeoutSeconds * 1000); // Convert to milliseconds
20
+ const user = await getLoggedInUser();
21
+
22
+ console.log(`āœ… Successfully logged in as ${user.email}`);
23
+
24
+ // Force immediate exit to prevent hanging (especially in development with tsx)
25
+ setTimeout(() => process.exit(0), 100);
26
+ } catch (error) {
27
+ console.error(
28
+ "āŒ Login failed:",
29
+ error instanceof Error ? error.message : "Unknown error",
30
+ );
31
+ process.exit(1);
32
+ }
33
+ });
34
+ }
@@ -0,0 +1,19 @@
1
+ import { Command } from "commander";
2
+ import { logout } from "@zapier/zapier-sdk";
3
+
4
+ export function createLogoutCommand(): Command {
5
+ return new Command("logout")
6
+ .description("Log out of your Zapier account")
7
+ .action(async () => {
8
+ try {
9
+ logout();
10
+ console.log("āœ… Successfully logged out");
11
+ } catch (error) {
12
+ console.error(
13
+ "āŒ Logout failed:",
14
+ error instanceof Error ? error.message : "Unknown error",
15
+ );
16
+ process.exit(1);
17
+ }
18
+ });
19
+ }
@@ -0,0 +1,25 @@
1
+ import { Command } from "commander";
2
+ import { getLoggedInUser } from "@zapier/zapier-sdk";
3
+ import { spinPromise } from "../utils/spinner";
4
+
5
+ export function createWhoamiCommand(): Command {
6
+ return new Command("whoami")
7
+ .description("Show current login status and user information")
8
+ .action(async () => {
9
+ try {
10
+ const user = await spinPromise(
11
+ getLoggedInUser(),
12
+ "Checking login status...",
13
+ );
14
+
15
+ console.log(
16
+ `āœ… Logged in as ${user.email} (Account ID: ${user.accountId})`,
17
+ );
18
+ } catch {
19
+ console.log(
20
+ "āŒ Not logged in. Use 'zapier-sdk login' to authenticate.",
21
+ );
22
+ process.exit(1);
23
+ }
24
+ });
25
+ }
@@ -0,0 +1,44 @@
1
+ // Simple HTTP client for OAuth authentication
2
+ export interface ApiResponse<T = any> {
3
+ data: T;
4
+ status: number;
5
+ }
6
+
7
+ export const createApiClient = () => {
8
+ const post = async <T = any>(
9
+ url: string,
10
+ data: any,
11
+ options: {
12
+ headers?: Record<string, string>;
13
+ } = {},
14
+ ): Promise<ApiResponse<T>> => {
15
+ const { headers = {} } = options;
16
+
17
+ const response = await fetch(url, {
18
+ method: "POST",
19
+ headers: {
20
+ "Content-Type": "application/x-www-form-urlencoded",
21
+ Connection: "close",
22
+ ...headers,
23
+ },
24
+ body: new URLSearchParams(data),
25
+ });
26
+
27
+ if (!response.ok) {
28
+ throw new Error(`${response.status} ${response.statusText}`);
29
+ }
30
+
31
+ const responseData = await response.json();
32
+ return {
33
+ data: responseData,
34
+ status: response.status,
35
+ };
36
+ };
37
+
38
+ return {
39
+ post,
40
+ };
41
+ };
42
+
43
+ const api = createApiClient();
44
+ export default api;