@zapier/zapier-sdk-cli 0.10.0 → 0.11.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 +12 -0
- package/dist/cli.cjs +106 -47
- package/dist/cli.mjs +106 -47
- package/dist/package.json +1 -1
- package/dist/src/utils/parameter-resolver.d.ts +5 -0
- package/dist/src/utils/parameter-resolver.js +102 -43
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +3 -3
- package/src/utils/parameter-resolver.ts +157 -61
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zapier/zapier-sdk-cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.11.0",
|
|
4
4
|
"description": "Command line interface for Zapier SDK",
|
|
5
5
|
"main": "dist/index.cjs",
|
|
6
6
|
"module": "dist/index.mjs",
|
|
@@ -42,9 +42,9 @@
|
|
|
42
42
|
"ora": "^8.2.0",
|
|
43
43
|
"pkce-challenge": "^5.0.0",
|
|
44
44
|
"zod": "^3.25.67",
|
|
45
|
-
"@zapier/zapier-sdk": "0.10.0",
|
|
46
45
|
"@zapier/zapier-sdk-cli-login": "0.3.2",
|
|
47
|
-
"@zapier/zapier-sdk-mcp": "0.3.
|
|
46
|
+
"@zapier/zapier-sdk-mcp": "0.3.8",
|
|
47
|
+
"@zapier/zapier-sdk": "0.11.0"
|
|
48
48
|
},
|
|
49
49
|
"devDependencies": {
|
|
50
50
|
"@types/express": "^5.0.3",
|
|
@@ -459,7 +459,7 @@ export class SchemaParameterResolver {
|
|
|
459
459
|
const inputs: Record<string, unknown> = {};
|
|
460
460
|
let processedFieldKeys = new Set<string>();
|
|
461
461
|
let iteration = 0;
|
|
462
|
-
const maxIterations =
|
|
462
|
+
const maxIterations = 10; // Prevent infinite loops
|
|
463
463
|
|
|
464
464
|
while (iteration < maxIterations) {
|
|
465
465
|
iteration++;
|
|
@@ -479,12 +479,12 @@ export class SchemaParameterResolver {
|
|
|
479
479
|
),
|
|
480
480
|
);
|
|
481
481
|
|
|
482
|
-
const
|
|
482
|
+
const rootFieldItems = await typedResolver.fetch(
|
|
483
483
|
updatedContext.sdk,
|
|
484
484
|
updatedContext.resolvedParams,
|
|
485
485
|
);
|
|
486
486
|
|
|
487
|
-
if (!
|
|
487
|
+
if (!rootFieldItems || rootFieldItems.length === 0) {
|
|
488
488
|
if (iteration === 1) {
|
|
489
489
|
console.log(
|
|
490
490
|
chalk.yellow(`No input fields required for this action.`),
|
|
@@ -493,59 +493,180 @@ export class SchemaParameterResolver {
|
|
|
493
493
|
break;
|
|
494
494
|
}
|
|
495
495
|
|
|
496
|
-
//
|
|
497
|
-
const
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
496
|
+
// Process fields recursively, maintaining fieldset structure
|
|
497
|
+
const fieldStats = await this.processFieldItems(
|
|
498
|
+
rootFieldItems,
|
|
499
|
+
inputs,
|
|
500
|
+
processedFieldKeys,
|
|
501
|
+
[],
|
|
502
|
+
iteration,
|
|
503
503
|
);
|
|
504
504
|
|
|
505
|
-
|
|
506
|
-
|
|
505
|
+
// If no new fields were processed, we're done
|
|
506
|
+
if (fieldStats.newRequired === 0 && fieldStats.newOptional === 0) {
|
|
507
507
|
break;
|
|
508
508
|
}
|
|
509
509
|
|
|
510
|
-
//
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
510
|
+
// If we only processed optional fields and skipped them, no need to re-fetch
|
|
511
|
+
if (fieldStats.newRequired === 0 && fieldStats.optionalSkipped) {
|
|
512
|
+
break;
|
|
513
|
+
}
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
if (iteration >= maxIterations) {
|
|
517
|
+
console.log(
|
|
518
|
+
chalk.yellow(
|
|
519
|
+
`\n⚠️ Maximum field resolution iterations reached. Some dynamic fields may not have been discovered.`,
|
|
520
|
+
),
|
|
516
521
|
);
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
return inputs;
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
/**
|
|
528
|
+
* Recursively processes fieldsets and their fields, maintaining natural structure
|
|
529
|
+
* and creating nested inputs as needed (e.g., fieldset "foo" becomes inputs.foo = [{}])
|
|
530
|
+
*/
|
|
531
|
+
private async processFieldItems(
|
|
532
|
+
items: unknown[],
|
|
533
|
+
targetInputs: Record<string, unknown>,
|
|
534
|
+
processedFieldKeys: Set<string>,
|
|
535
|
+
fieldsetPath: string[] = [],
|
|
536
|
+
iteration: number = 1,
|
|
537
|
+
): Promise<{
|
|
538
|
+
newRequired: number;
|
|
539
|
+
newOptional: number;
|
|
540
|
+
optionalSkipped: boolean;
|
|
541
|
+
}> {
|
|
542
|
+
let newRequiredCount = 0;
|
|
543
|
+
let newOptionalCount = 0;
|
|
544
|
+
let optionalSkipped = false;
|
|
545
|
+
|
|
546
|
+
for (const item of items) {
|
|
547
|
+
const typedItem = item as {
|
|
548
|
+
type?: string;
|
|
549
|
+
key?: string;
|
|
550
|
+
title?: string;
|
|
551
|
+
fields?: unknown[];
|
|
552
|
+
is_required?: boolean;
|
|
553
|
+
};
|
|
517
554
|
|
|
518
|
-
|
|
519
|
-
|
|
555
|
+
if (typedItem.type === "fieldset" && typedItem.fields && typedItem.key) {
|
|
556
|
+
// Show fieldset context to user
|
|
557
|
+
const fieldsetTitle = typedItem.title || typedItem.key;
|
|
558
|
+
const pathDisplay =
|
|
559
|
+
fieldsetPath.length > 0 ? ` (in ${fieldsetPath.join(" > ")})` : "";
|
|
520
560
|
console.log(
|
|
521
|
-
chalk.
|
|
522
|
-
`\n
|
|
561
|
+
chalk.cyan(
|
|
562
|
+
`\n📁 Processing fieldset: ${fieldsetTitle}${pathDisplay}`,
|
|
523
563
|
),
|
|
524
564
|
);
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
565
|
+
|
|
566
|
+
// Create fieldset array in target inputs if it doesn't exist
|
|
567
|
+
if (!targetInputs[typedItem.key]) {
|
|
568
|
+
targetInputs[typedItem.key] = [{}];
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
// Process fields within this fieldset recursively
|
|
572
|
+
const fieldsetTarget = (
|
|
573
|
+
targetInputs[typedItem.key] as Record<string, unknown>[]
|
|
574
|
+
)[0];
|
|
575
|
+
const nestedPath = [...fieldsetPath, fieldsetTitle];
|
|
576
|
+
|
|
577
|
+
const nestedStats = await this.processFieldItems(
|
|
578
|
+
typedItem.fields,
|
|
579
|
+
fieldsetTarget,
|
|
580
|
+
processedFieldKeys,
|
|
581
|
+
nestedPath,
|
|
582
|
+
iteration,
|
|
583
|
+
);
|
|
584
|
+
|
|
585
|
+
newRequiredCount += nestedStats.newRequired;
|
|
586
|
+
newOptionalCount += nestedStats.newOptional;
|
|
587
|
+
if (nestedStats.optionalSkipped) {
|
|
588
|
+
optionalSkipped = true;
|
|
589
|
+
}
|
|
590
|
+
} else if (typedItem.type === "input_field" && typedItem.key) {
|
|
591
|
+
// Skip if already processed
|
|
592
|
+
if (processedFieldKeys.has(typedItem.key)) {
|
|
593
|
+
continue;
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
const isRequired = typedItem.is_required || false;
|
|
597
|
+
|
|
598
|
+
if (isRequired) {
|
|
599
|
+
// Process required field immediately
|
|
600
|
+
newRequiredCount++;
|
|
601
|
+
if (newRequiredCount === 1 && fieldsetPath.length === 0) {
|
|
602
|
+
// Only show this message once at root level
|
|
603
|
+
console.log(
|
|
604
|
+
chalk.blue(
|
|
605
|
+
`\n📝 Please provide values for the following ${iteration === 1 ? "" : "additional "}input fields:`,
|
|
606
|
+
),
|
|
607
|
+
);
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
await this.promptForField(typedItem, targetInputs);
|
|
611
|
+
processedFieldKeys.add(typedItem.key);
|
|
612
|
+
} else {
|
|
613
|
+
// Collect optional fields for batch processing
|
|
614
|
+
newOptionalCount++;
|
|
528
615
|
}
|
|
529
616
|
}
|
|
617
|
+
// Skip info fields - they're for display only
|
|
618
|
+
}
|
|
530
619
|
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
620
|
+
// Handle optional fields after processing all required fields
|
|
621
|
+
if (newOptionalCount > 0) {
|
|
622
|
+
const optionalFields = items.filter((item: unknown) => {
|
|
623
|
+
const typedItem = item as {
|
|
624
|
+
type?: string;
|
|
625
|
+
key?: string;
|
|
626
|
+
is_required?: boolean;
|
|
627
|
+
};
|
|
628
|
+
return (
|
|
629
|
+
typedItem.type === "input_field" &&
|
|
630
|
+
typedItem.key &&
|
|
631
|
+
!typedItem.is_required &&
|
|
632
|
+
!processedFieldKeys.has(typedItem.key)
|
|
633
|
+
);
|
|
634
|
+
});
|
|
635
|
+
|
|
636
|
+
if (optionalFields.length > 0) {
|
|
637
|
+
const pathContext =
|
|
638
|
+
fieldsetPath.length > 0 ? ` in ${fieldsetPath.join(" > ")}` : "";
|
|
534
639
|
console.log(
|
|
535
640
|
chalk.gray(
|
|
536
|
-
`\nThere are ${
|
|
641
|
+
`\nThere are ${optionalFields.length} ${iteration === 1 ? "" : "additional "}optional field(s) available${pathContext}.`,
|
|
537
642
|
),
|
|
538
643
|
);
|
|
539
644
|
|
|
540
645
|
try {
|
|
541
|
-
shouldConfigureOptional = await inquirer.prompt([
|
|
646
|
+
const shouldConfigureOptional = await inquirer.prompt([
|
|
542
647
|
{
|
|
543
648
|
type: "confirm",
|
|
544
649
|
name: "configure",
|
|
545
|
-
message: `Would you like to configure ${iteration === 1 ? "" : "these additional "}optional fields?`,
|
|
650
|
+
message: `Would you like to configure ${iteration === 1 ? "" : "these additional "}optional fields${pathContext}?`,
|
|
546
651
|
default: false,
|
|
547
652
|
},
|
|
548
653
|
]);
|
|
654
|
+
|
|
655
|
+
if (shouldConfigureOptional.configure) {
|
|
656
|
+
console.log(chalk.cyan(`\nOptional fields${pathContext}:`));
|
|
657
|
+
for (const field of optionalFields) {
|
|
658
|
+
await this.promptForField(field, targetInputs);
|
|
659
|
+
const typedField = field as { key: string };
|
|
660
|
+
processedFieldKeys.add(typedField.key);
|
|
661
|
+
}
|
|
662
|
+
} else {
|
|
663
|
+
optionalSkipped = true;
|
|
664
|
+
// Mark these fields as processed even if skipped to avoid re-asking
|
|
665
|
+
optionalFields.forEach((field: unknown) => {
|
|
666
|
+
const typedField = field as { key: string };
|
|
667
|
+
processedFieldKeys.add(typedField.key);
|
|
668
|
+
});
|
|
669
|
+
}
|
|
549
670
|
} catch (error) {
|
|
550
671
|
if (this.isUserCancellation(error)) {
|
|
551
672
|
console.log(chalk.yellow("\n\nOperation cancelled by user"));
|
|
@@ -553,39 +674,14 @@ export class SchemaParameterResolver {
|
|
|
553
674
|
}
|
|
554
675
|
throw error;
|
|
555
676
|
}
|
|
556
|
-
|
|
557
|
-
if (shouldConfigureOptional.configure) {
|
|
558
|
-
console.log(chalk.cyan(`\nOptional fields:`));
|
|
559
|
-
for (const field of newOptionalFields) {
|
|
560
|
-
await this.promptForField(field, inputs);
|
|
561
|
-
processedFieldKeys.add((field as { key: string }).key);
|
|
562
|
-
}
|
|
563
|
-
} else {
|
|
564
|
-
// Mark these fields as processed even if skipped to avoid re-asking
|
|
565
|
-
newOptionalFields.forEach((field: unknown) =>
|
|
566
|
-
processedFieldKeys.add((field as { key: string }).key),
|
|
567
|
-
);
|
|
568
|
-
}
|
|
569
|
-
}
|
|
570
|
-
|
|
571
|
-
// If we only processed optional fields and skipped them, no need to re-fetch
|
|
572
|
-
if (
|
|
573
|
-
newRequiredFields.length === 0 &&
|
|
574
|
-
(!newOptionalFields.length || !shouldConfigureOptional.configure)
|
|
575
|
-
) {
|
|
576
|
-
break;
|
|
577
677
|
}
|
|
578
678
|
}
|
|
579
679
|
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
);
|
|
586
|
-
}
|
|
587
|
-
|
|
588
|
-
return inputs;
|
|
680
|
+
return {
|
|
681
|
+
newRequired: newRequiredCount,
|
|
682
|
+
newOptional: newOptionalCount,
|
|
683
|
+
optionalSkipped,
|
|
684
|
+
};
|
|
589
685
|
}
|
|
590
686
|
|
|
591
687
|
private getNestedValue(obj: unknown, path: string[]): unknown {
|