@zapier/zapier-sdk-cli 0.4.4 → 0.6.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/CHANGELOG.md +30 -0
- package/README.md +92 -72
- package/dist/cli.js +212 -67
- package/dist/src/cli.js +1 -1
- package/dist/src/commands/bundle-code/cli.js +1 -1
- package/dist/src/commands/generate-types/cli.js +17 -6
- package/dist/src/commands/generate-types/index.js +26 -5
- package/dist/src/commands/generate-types/schemas.d.ts +3 -0
- package/dist/src/commands/generate-types/schemas.js +4 -0
- package/dist/src/utils/api/client.d.ts +3 -3
- package/dist/src/utils/cli-generator-utils.d.ts +2 -2
- package/dist/src/utils/cli-generator.js +84 -23
- package/dist/src/utils/log.d.ts +4 -4
- package/dist/src/utils/parameter-resolver.d.ts +1 -1
- package/dist/src/utils/parameter-resolver.js +45 -30
- package/dist/src/utils/schema-formatter.d.ts +1 -1
- package/dist/src/utils/schema-formatter.js +6 -4
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +4 -4
- package/src/cli.test.ts +1 -2
- package/src/cli.ts +1 -1
- package/src/commands/bundle-code/cli.ts +19 -4
- package/src/commands/generate-types/cli.ts +25 -9
- package/src/commands/generate-types/index.ts +32 -6
- package/src/commands/generate-types/schemas.ts +4 -0
- package/src/utils/api/client.ts +3 -3
- package/src/utils/auth/login.ts +2 -1
- package/src/utils/cli-generator-utils.ts +7 -7
- package/src/utils/cli-generator.ts +154 -48
- package/src/utils/log.ts +4 -4
- package/src/utils/parameter-resolver.ts +106 -55
- package/src/utils/schema-formatter.ts +20 -11
|
@@ -13,7 +13,9 @@ function formatJsonOutput(data) {
|
|
|
13
13
|
if (data &&
|
|
14
14
|
typeof data === "object" &&
|
|
15
15
|
!Array.isArray(data) &&
|
|
16
|
-
(data.success !== undefined ||
|
|
16
|
+
(data.success !== undefined ||
|
|
17
|
+
data.id ||
|
|
18
|
+
data.status)) {
|
|
17
19
|
console.log(chalk.green("✅ Action completed successfully!\n"));
|
|
18
20
|
}
|
|
19
21
|
// Use util.inspect for colored output
|
|
@@ -106,12 +108,13 @@ function methodNameToCliCommand(methodName) {
|
|
|
106
108
|
// ============================================================================
|
|
107
109
|
export function generateCliCommands(program, sdk) {
|
|
108
110
|
// Check if SDK has registry
|
|
109
|
-
if (
|
|
111
|
+
if (typeof sdk.getRegistry !== "function") {
|
|
110
112
|
console.error("SDK registry not available");
|
|
111
113
|
return;
|
|
112
114
|
}
|
|
113
|
-
|
|
114
|
-
|
|
115
|
+
const registry = sdk.getRegistry();
|
|
116
|
+
// Create all commands first
|
|
117
|
+
registry.functions.forEach((fnInfo) => {
|
|
115
118
|
if (!fnInfo.inputSchema) {
|
|
116
119
|
console.warn(`Schema not found for ${fnInfo.name}`);
|
|
117
120
|
return;
|
|
@@ -121,6 +124,57 @@ export function generateCliCommands(program, sdk) {
|
|
|
121
124
|
const config = createCommandConfig(cliCommandName, fnInfo.name, fnInfo.inputSchema, sdk);
|
|
122
125
|
addCommand(program, cliCommandName, config);
|
|
123
126
|
});
|
|
127
|
+
// Override the help display to show commands grouped by category
|
|
128
|
+
program.configureHelp({
|
|
129
|
+
formatHelp: (cmd, helper) => {
|
|
130
|
+
const helpWidth = helper.helpWidth || 80;
|
|
131
|
+
let output = helper.commandUsage(cmd) + "\n";
|
|
132
|
+
if (cmd.description()) {
|
|
133
|
+
output += helper.wrap(cmd.description(), helpWidth, 0) + "\n";
|
|
134
|
+
}
|
|
135
|
+
// Add options section
|
|
136
|
+
const options = helper.visibleOptions(cmd);
|
|
137
|
+
if (options.length > 0) {
|
|
138
|
+
output += "\nOptions:\n";
|
|
139
|
+
const longestOptionLength = Math.max(...options.map((opt) => helper.optionTerm(opt).length));
|
|
140
|
+
options.forEach((option) => {
|
|
141
|
+
const term = helper.optionTerm(option);
|
|
142
|
+
const padding = " ".repeat(Math.max(2, longestOptionLength - term.length + 4));
|
|
143
|
+
output += ` ${term}${padding}${helper.optionDescription(option)}\n`;
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
// Add categorized commands section
|
|
147
|
+
const commands = helper.visibleCommands(cmd);
|
|
148
|
+
if (commands.length > 0) {
|
|
149
|
+
output += "\nCommands:\n";
|
|
150
|
+
// Collect all SDK commands that belong to categories
|
|
151
|
+
const categorizedCommands = new Set();
|
|
152
|
+
// Group SDK commands by categories
|
|
153
|
+
registry.categories.forEach((category) => {
|
|
154
|
+
const categoryCommands = commands.filter((command) => category.functions.some((functionName) => {
|
|
155
|
+
const cliCommandName = methodNameToCliCommand(functionName);
|
|
156
|
+
return command.name() === cliCommandName;
|
|
157
|
+
}));
|
|
158
|
+
if (categoryCommands.length > 0) {
|
|
159
|
+
output += `\n ${category.titlePlural}:\n`;
|
|
160
|
+
categoryCommands.forEach((command) => {
|
|
161
|
+
output += ` ${helper.subcommandTerm(command)}\n`;
|
|
162
|
+
categorizedCommands.add(command.name());
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
});
|
|
166
|
+
// Add any remaining commands that aren't part of SDK categories
|
|
167
|
+
const otherCommands = commands.filter((command) => !categorizedCommands.has(command.name()));
|
|
168
|
+
if (otherCommands.length > 0) {
|
|
169
|
+
output += `\n Other:\n`;
|
|
170
|
+
otherCommands.forEach((command) => {
|
|
171
|
+
output += ` ${helper.subcommandTerm(command)}\n`;
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
return output;
|
|
176
|
+
},
|
|
177
|
+
});
|
|
124
178
|
}
|
|
125
179
|
function createCommandConfig(cliCommandName, sdkMethodName, schema, sdk) {
|
|
126
180
|
const parameters = analyzeZodSchema(schema);
|
|
@@ -140,28 +194,33 @@ function createCommandConfig(cliCommandName, sdkMethodName, schema, sdk) {
|
|
|
140
194
|
// Resolve missing parameters interactively using schema metadata
|
|
141
195
|
const resolver = new SchemaParameterResolver();
|
|
142
196
|
const resolvedParams = await resolver.resolveParameters(schema, rawParams, sdk);
|
|
143
|
-
// Special handling for commands that write to files
|
|
144
|
-
const hasOutputFile = resolvedParams.output;
|
|
145
|
-
if (hasOutputFile) {
|
|
146
|
-
// Call the SDK method for file output commands
|
|
147
|
-
await sdk[sdkMethodName](resolvedParams);
|
|
148
|
-
console.log(chalk.green(`✅ ${cliCommandName} completed successfully!`));
|
|
149
|
-
console.log(chalk.gray(`Output written to: ${resolvedParams.output}`));
|
|
150
|
-
return;
|
|
151
|
-
}
|
|
152
197
|
// Handle paginated list commands with async iteration
|
|
153
198
|
if (isListCommand &&
|
|
154
199
|
hasPaginationParams &&
|
|
155
200
|
!shouldUseJson &&
|
|
156
201
|
!hasUserSpecifiedMaxItems) {
|
|
157
202
|
// Get the async iterator directly from the SDK method call
|
|
158
|
-
const
|
|
203
|
+
const sdkObj = sdk;
|
|
204
|
+
const sdkIterator = await sdkObj[sdkMethodName](resolvedParams);
|
|
159
205
|
await handlePaginatedListWithAsyncIteration(sdkMethodName, sdkIterator, schema);
|
|
160
206
|
}
|
|
161
207
|
else {
|
|
208
|
+
// Special handling for commands that write to files
|
|
209
|
+
const hasOutputFile = resolvedParams.output;
|
|
210
|
+
if (hasOutputFile) {
|
|
211
|
+
// Call the SDK method for file output commands
|
|
212
|
+
const sdkObj = sdk;
|
|
213
|
+
await sdkObj[sdkMethodName](resolvedParams);
|
|
214
|
+
console.log(chalk.green(`✅ ${cliCommandName} completed successfully!`));
|
|
215
|
+
console.log(chalk.gray(`Output written to: ${hasOutputFile}`));
|
|
216
|
+
return;
|
|
217
|
+
}
|
|
162
218
|
// Call the SDK method and handle non-paginated results
|
|
163
|
-
const
|
|
164
|
-
const
|
|
219
|
+
const sdkObj = sdk;
|
|
220
|
+
const result = await sdkObj[sdkMethodName](resolvedParams);
|
|
221
|
+
const items = result?.data
|
|
222
|
+
? result.data
|
|
223
|
+
: result;
|
|
165
224
|
if (shouldUseJson) {
|
|
166
225
|
console.log(JSON.stringify(items, null, 2));
|
|
167
226
|
}
|
|
@@ -180,8 +239,9 @@ function createCommandConfig(cliCommandName, sdkMethodName, schema, sdk) {
|
|
|
180
239
|
const validationErrors = JSON.parse(error.message);
|
|
181
240
|
console.error(chalk.red("❌ Validation Error:"));
|
|
182
241
|
validationErrors.forEach((err) => {
|
|
183
|
-
const
|
|
184
|
-
|
|
242
|
+
const errorObj = err;
|
|
243
|
+
const field = errorObj?.path?.join(".") || "unknown";
|
|
244
|
+
console.error(chalk.yellow(` • ${field}: ${errorObj?.message || "Unknown error"}`));
|
|
185
245
|
});
|
|
186
246
|
console.error("\n" + chalk.dim(`Use --help to see available options`));
|
|
187
247
|
}
|
|
@@ -239,7 +299,7 @@ function addCommand(program, commandName, config) {
|
|
|
239
299
|
}
|
|
240
300
|
else {
|
|
241
301
|
const flagSignature = flags.join(", ") + ` <${param.type}>`;
|
|
242
|
-
command.option(flagSignature, param.description, param.default);
|
|
302
|
+
command.option(flagSignature, param.description || "", param.default);
|
|
243
303
|
}
|
|
244
304
|
}
|
|
245
305
|
});
|
|
@@ -415,10 +475,11 @@ function formatNonPaginatedResults(result, requestedMaxItems, userSpecifiedMaxIt
|
|
|
415
475
|
function formatItemsGeneric(items) {
|
|
416
476
|
// Fallback formatting for items without schema metadata
|
|
417
477
|
items.forEach((item, index) => {
|
|
418
|
-
const
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
478
|
+
const itemObj = item;
|
|
479
|
+
const name = itemObj?.name || itemObj?.key || itemObj?.id || "Item";
|
|
480
|
+
console.log(`${chalk.gray(`${index + 1}.`)} ${chalk.cyan(String(name))}`);
|
|
481
|
+
if (itemObj?.description) {
|
|
482
|
+
console.log(` ${chalk.dim(String(itemObj.description))}`);
|
|
422
483
|
}
|
|
423
484
|
console.log();
|
|
424
485
|
});
|
package/dist/src/utils/log.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
declare const log: {
|
|
2
|
-
info: (message: string, ...args:
|
|
3
|
-
error: (message: string, ...args:
|
|
4
|
-
success: (message: string, ...args:
|
|
5
|
-
warn: (message: string, ...args:
|
|
2
|
+
info: (message: string, ...args: unknown[]) => void;
|
|
3
|
+
error: (message: string, ...args: unknown[]) => void;
|
|
4
|
+
success: (message: string, ...args: unknown[]) => void;
|
|
5
|
+
warn: (message: string, ...args: unknown[]) => void;
|
|
6
6
|
};
|
|
7
7
|
export default log;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { ZapierSdk } from "@zapier/zapier-sdk";
|
|
3
3
|
export declare class SchemaParameterResolver {
|
|
4
|
-
resolveParameters(schema: z.ZodSchema, providedParams:
|
|
4
|
+
resolveParameters(schema: z.ZodSchema, providedParams: unknown, sdk: ZapierSdk): Promise<unknown>;
|
|
5
5
|
private extractParametersFromSchema;
|
|
6
6
|
private analyzeFieldSchema;
|
|
7
7
|
private createResolvableParameter;
|
|
@@ -55,7 +55,7 @@ export class SchemaParameterResolver {
|
|
|
55
55
|
}
|
|
56
56
|
return parseResult.data;
|
|
57
57
|
}
|
|
58
|
-
// 2. Resolve functionally required parameters
|
|
58
|
+
// 2. Resolve functionally required parameters first
|
|
59
59
|
const resolvedParams = { ...providedParams };
|
|
60
60
|
const context = {
|
|
61
61
|
sdk,
|
|
@@ -214,28 +214,31 @@ export class SchemaParameterResolver {
|
|
|
214
214
|
throw new Error(`No resolver found for parameter: ${param.name}`);
|
|
215
215
|
}
|
|
216
216
|
console.log(chalk.blue(`\n🔍 Resolving ${param.name}...`));
|
|
217
|
-
|
|
217
|
+
const typedResolver = resolver;
|
|
218
|
+
if (typedResolver.type === "static") {
|
|
218
219
|
// Static resolver - just prompt for input
|
|
219
220
|
const promptConfig = {
|
|
220
|
-
type:
|
|
221
|
+
type: typedResolver.inputType === "password" ? "password" : "input",
|
|
221
222
|
name: param.name,
|
|
222
223
|
message: `Enter ${param.name}:`,
|
|
223
|
-
...(
|
|
224
|
+
...(typedResolver.placeholder && {
|
|
225
|
+
default: typedResolver.placeholder,
|
|
226
|
+
}),
|
|
224
227
|
};
|
|
225
228
|
const answers = await inquirer.prompt([promptConfig]);
|
|
226
229
|
return answers[param.name];
|
|
227
230
|
}
|
|
228
|
-
else if (
|
|
231
|
+
else if (typedResolver.type === "dynamic") {
|
|
229
232
|
// Dynamic resolver - fetch options and prompt for selection
|
|
230
233
|
try {
|
|
231
234
|
// Only show "Fetching..." for required parameters that typically have many options
|
|
232
235
|
if (param.isRequired && param.name !== "authenticationId") {
|
|
233
236
|
console.log(chalk.gray(`Fetching options for ${param.name}...`));
|
|
234
237
|
}
|
|
235
|
-
const items = await
|
|
238
|
+
const items = await typedResolver.fetch(context.sdk, context.resolvedParams);
|
|
236
239
|
// Let the resolver's prompt handle empty lists (e.g., authenticationId can show "skip authentication")
|
|
237
240
|
const safeItems = items || [];
|
|
238
|
-
const promptConfig =
|
|
241
|
+
const promptConfig = typedResolver.prompt(safeItems, context.resolvedParams);
|
|
239
242
|
const answers = await inquirer.prompt([promptConfig]);
|
|
240
243
|
return answers[param.name];
|
|
241
244
|
}
|
|
@@ -244,13 +247,14 @@ export class SchemaParameterResolver {
|
|
|
244
247
|
throw error;
|
|
245
248
|
}
|
|
246
249
|
}
|
|
247
|
-
else if (
|
|
250
|
+
else if (typedResolver.type === "fields") {
|
|
248
251
|
// Fields resolver - fetch field definitions and prompt for each input with recursive field resolution
|
|
249
252
|
return await this.resolveFieldsRecursively(resolver, context, param);
|
|
250
253
|
}
|
|
251
254
|
throw new Error(`Unknown resolver type for ${param.name}`);
|
|
252
255
|
}
|
|
253
256
|
async resolveFieldsRecursively(resolver, context, param) {
|
|
257
|
+
const typedResolver = resolver;
|
|
254
258
|
const inputs = {};
|
|
255
259
|
let processedFieldKeys = new Set();
|
|
256
260
|
let iteration = 0;
|
|
@@ -266,7 +270,7 @@ export class SchemaParameterResolver {
|
|
|
266
270
|
},
|
|
267
271
|
};
|
|
268
272
|
console.log(chalk.gray(`Fetching input fields for ${param.name}${iteration > 1 ? ` (iteration ${iteration})` : ""}...`));
|
|
269
|
-
const fields = await
|
|
273
|
+
const fields = await typedResolver.fetch(updatedContext.sdk, updatedContext.resolvedParams);
|
|
270
274
|
if (!fields || fields.length === 0) {
|
|
271
275
|
if (iteration === 1) {
|
|
272
276
|
console.log(chalk.yellow(`No input fields required for this action.`));
|
|
@@ -274,7 +278,8 @@ export class SchemaParameterResolver {
|
|
|
274
278
|
break;
|
|
275
279
|
}
|
|
276
280
|
// Find new fields that we haven't processed yet
|
|
277
|
-
const newFields = fields.filter((field) => !
|
|
281
|
+
const newFields = fields.filter((field) => !(field.key &&
|
|
282
|
+
processedFieldKeys.has(field.key)));
|
|
278
283
|
if (newFields.length === 0) {
|
|
279
284
|
// No new fields, we're done
|
|
280
285
|
break;
|
|
@@ -340,35 +345,44 @@ export class SchemaParameterResolver {
|
|
|
340
345
|
setNestedValue(obj, path, value) {
|
|
341
346
|
const lastKey = path[path.length - 1];
|
|
342
347
|
const parent = path.slice(0, -1).reduce((current, key) => {
|
|
343
|
-
|
|
344
|
-
|
|
348
|
+
const currentObj = current;
|
|
349
|
+
if (!(key in currentObj)) {
|
|
350
|
+
currentObj[key] = {};
|
|
345
351
|
}
|
|
346
|
-
return
|
|
352
|
+
return currentObj[key];
|
|
347
353
|
}, obj);
|
|
348
354
|
parent[lastKey] = value;
|
|
349
355
|
}
|
|
350
356
|
async promptForField(field, inputs) {
|
|
357
|
+
const fieldObj = field;
|
|
351
358
|
const fieldPrompt = {
|
|
352
|
-
type:
|
|
353
|
-
name:
|
|
354
|
-
message: `${
|
|
355
|
-
...(field.helpText && { prefix: chalk.gray(`ℹ ${field.helpText}\n`) }),
|
|
356
|
-
...(field.default && { default: field.default }),
|
|
359
|
+
type: fieldObj.type === "boolean" ? "confirm" : "input",
|
|
360
|
+
name: fieldObj.key,
|
|
361
|
+
message: `${fieldObj.label || fieldObj.key}${fieldObj.required ? " (required)" : " (optional)"}:`,
|
|
357
362
|
};
|
|
358
|
-
if (
|
|
363
|
+
if (fieldObj.helpText) {
|
|
364
|
+
fieldPrompt.prefix = chalk.gray(`ℹ ${fieldObj.helpText}\n`);
|
|
365
|
+
}
|
|
366
|
+
if (fieldObj.default !== undefined) {
|
|
367
|
+
fieldPrompt.default = fieldObj.default;
|
|
368
|
+
}
|
|
369
|
+
if (fieldObj.choices && fieldObj.choices.length > 0) {
|
|
359
370
|
fieldPrompt.type = "list";
|
|
360
|
-
fieldPrompt.choices =
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
371
|
+
fieldPrompt.choices = fieldObj.choices.map((choice) => {
|
|
372
|
+
const choiceObj = choice;
|
|
373
|
+
return {
|
|
374
|
+
name: choiceObj.label || choiceObj.value,
|
|
375
|
+
value: choiceObj.value,
|
|
376
|
+
};
|
|
377
|
+
});
|
|
364
378
|
}
|
|
365
379
|
try {
|
|
366
380
|
const answer = await inquirer.prompt([fieldPrompt]);
|
|
367
|
-
if (answer[
|
|
368
|
-
inputs[
|
|
381
|
+
if (answer[fieldObj.key] !== undefined && answer[fieldObj.key] !== "") {
|
|
382
|
+
inputs[fieldObj.key] = answer[fieldObj.key];
|
|
369
383
|
}
|
|
370
|
-
else if (
|
|
371
|
-
throw new Error(`Required field ${
|
|
384
|
+
else if (fieldObj.required) {
|
|
385
|
+
throw new Error(`Required field ${fieldObj.key} cannot be empty`);
|
|
372
386
|
}
|
|
373
387
|
}
|
|
374
388
|
catch (error) {
|
|
@@ -380,8 +394,9 @@ export class SchemaParameterResolver {
|
|
|
380
394
|
}
|
|
381
395
|
}
|
|
382
396
|
isUserCancellation(error) {
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
397
|
+
const errorObj = error;
|
|
398
|
+
return (errorObj?.name === "ExitPromptError" ||
|
|
399
|
+
errorObj?.message?.includes("User force closed") ||
|
|
400
|
+
errorObj?.isTTYError === true);
|
|
386
401
|
}
|
|
387
402
|
}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
-
export declare function formatItemsFromSchema(inputSchema: z.ZodType, items:
|
|
2
|
+
export declare function formatItemsFromSchema(inputSchema: z.ZodType, items: unknown[]): void;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import chalk from "chalk";
|
|
2
2
|
function getFormatMetadata(schema) {
|
|
3
|
-
return schema?._def
|
|
3
|
+
return schema?._def
|
|
4
|
+
?.formatMeta;
|
|
4
5
|
}
|
|
5
6
|
function getOutputSchema(schema) {
|
|
6
7
|
return schema?._def?.outputSchema;
|
|
@@ -61,10 +62,11 @@ function applyStyle(value, style) {
|
|
|
61
62
|
function formatItemsGeneric(items) {
|
|
62
63
|
// Fallback formatting for items without schema metadata
|
|
63
64
|
items.forEach((item, index) => {
|
|
64
|
-
const
|
|
65
|
+
const itemObj = item;
|
|
66
|
+
const name = itemObj.title || itemObj.name || itemObj.key || itemObj.id || "Item";
|
|
65
67
|
console.log(`${chalk.gray(`${index + 1}.`)} ${chalk.cyan(name)}`);
|
|
66
|
-
if (
|
|
67
|
-
console.log(` ${chalk.dim(
|
|
68
|
+
if (itemObj.description) {
|
|
69
|
+
console.log(` ${chalk.dim(itemObj.description)}`);
|
|
68
70
|
}
|
|
69
71
|
console.log();
|
|
70
72
|
});
|