@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.
- package/dist/cli.js +1099 -16
- package/dist/index.js +0 -3
- package/package.json +5 -3
- package/src/cli.ts +2 -2
- package/src/commands/configPath.ts +1 -1
- package/src/commands/index.ts +4 -4
- package/src/commands/login.ts +2 -2
- package/src/commands/logout.ts +1 -1
- package/src/commands/whoami.ts +2 -2
- package/src/utils/auth/login.ts +6 -6
- package/src/utils/cli-generator.ts +3 -3
- package/tsconfig.json +3 -3
- package/tsup.config.ts +17 -0
- package/dist/cli.d.ts +0 -2
- package/dist/commands/configPath.d.ts +0 -2
- package/dist/commands/configPath.js +0 -9
- package/dist/commands/index.d.ts +0 -4
- package/dist/commands/index.js +0 -4
- package/dist/commands/login.d.ts +0 -2
- package/dist/commands/login.js +0 -25
- package/dist/commands/logout.d.ts +0 -2
- package/dist/commands/logout.js +0 -16
- package/dist/commands/whoami.d.ts +0 -2
- package/dist/commands/whoami.js +0 -17
- package/dist/index.d.ts +0 -1
- package/dist/utils/api/client.d.ts +0 -15
- package/dist/utils/api/client.js +0 -27
- package/dist/utils/auth/login.d.ts +0 -2
- package/dist/utils/auth/login.js +0 -134
- package/dist/utils/cli-generator.d.ts +0 -3
- package/dist/utils/cli-generator.js +0 -388
- package/dist/utils/constants.d.ts +0 -5
- package/dist/utils/constants.js +0 -6
- package/dist/utils/getCallablePromise.d.ts +0 -6
- package/dist/utils/getCallablePromise.js +0 -14
- package/dist/utils/log.d.ts +0 -7
- package/dist/utils/log.js +0 -16
- package/dist/utils/pager.d.ts +0 -48
- package/dist/utils/pager.js +0 -137
- package/dist/utils/parameter-resolver.d.ts +0 -13
- package/dist/utils/parameter-resolver.js +0 -300
- package/dist/utils/schema-formatter.d.ts +0 -2
- package/dist/utils/schema-formatter.js +0 -71
- package/dist/utils/serializeAsync.d.ts +0 -2
- package/dist/utils/serializeAsync.js +0 -16
- package/dist/utils/spinner.d.ts +0 -1
- package/dist/utils/spinner.js +0 -13
package/dist/cli.js
CHANGED
|
@@ -1,25 +1,1108 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
|
|
2
|
+
|
|
3
|
+
// src/cli.ts
|
|
4
|
+
import { Command as Command5 } from "commander";
|
|
3
5
|
import { createZapierSdk } from "@zapier/zapier-sdk";
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
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();
|