@zapier/zapier-sdk-cli 0.15.1 → 0.15.3
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 +15 -0
- package/dist/cli.cjs +205 -34
- package/dist/cli.mjs +205 -34
- package/dist/index.cjs +10 -2
- package/dist/index.mjs +10 -2
- package/dist/package.json +1 -1
- package/dist/src/plugins/login/index.js +11 -0
- package/dist/src/utils/parameter-resolver.d.ts +20 -0
- package/dist/src/utils/parameter-resolver.js +189 -29
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +4 -4
- package/src/plugins/login/index.ts +11 -0
- package/src/utils/parameter-resolver.ts +275 -32
package/dist/index.cjs
CHANGED
|
@@ -291,7 +291,7 @@ var LoginSchema = zod.z.object({
|
|
|
291
291
|
|
|
292
292
|
// package.json
|
|
293
293
|
var package_default = {
|
|
294
|
-
version: "0.15.
|
|
294
|
+
version: "0.15.3"};
|
|
295
295
|
|
|
296
296
|
// src/telemetry/builders.ts
|
|
297
297
|
function createCliBaseEvent(context = {}) {
|
|
@@ -370,6 +370,8 @@ var loginPlugin = ({ context }) => {
|
|
|
370
370
|
const startTime = Date.now();
|
|
371
371
|
let success = false;
|
|
372
372
|
let errorMessage = null;
|
|
373
|
+
let accountId = null;
|
|
374
|
+
let customUserId = null;
|
|
373
375
|
try {
|
|
374
376
|
await loginWithSdk({
|
|
375
377
|
...options,
|
|
@@ -378,6 +380,10 @@ var loginPlugin = ({ context }) => {
|
|
|
378
380
|
authClientId: context.options?.authClientId
|
|
379
381
|
});
|
|
380
382
|
success = true;
|
|
383
|
+
try {
|
|
384
|
+
({ accountId, customUserId } = await zapierSdkCliLogin.getLoggedInUser());
|
|
385
|
+
} catch {
|
|
386
|
+
}
|
|
381
387
|
} catch (error) {
|
|
382
388
|
success = false;
|
|
383
389
|
errorMessage = error instanceof Error ? error.message : "Login failed";
|
|
@@ -401,7 +407,9 @@ var loginPlugin = ({ context }) => {
|
|
|
401
407
|
session_id: context.session_id,
|
|
402
408
|
selected_api: context.selected_api,
|
|
403
409
|
app_id: context.app_id,
|
|
404
|
-
app_version_id: context.app_version_id
|
|
410
|
+
app_version_id: context.app_version_id,
|
|
411
|
+
customuser_id: customUserId,
|
|
412
|
+
account_id: accountId
|
|
405
413
|
},
|
|
406
414
|
cliVersion: package_default.version
|
|
407
415
|
});
|
package/dist/index.mjs
CHANGED
|
@@ -260,7 +260,7 @@ var LoginSchema = z.object({
|
|
|
260
260
|
|
|
261
261
|
// package.json
|
|
262
262
|
var package_default = {
|
|
263
|
-
version: "0.15.
|
|
263
|
+
version: "0.15.3"};
|
|
264
264
|
|
|
265
265
|
// src/telemetry/builders.ts
|
|
266
266
|
function createCliBaseEvent(context = {}) {
|
|
@@ -339,6 +339,8 @@ var loginPlugin = ({ context }) => {
|
|
|
339
339
|
const startTime = Date.now();
|
|
340
340
|
let success = false;
|
|
341
341
|
let errorMessage = null;
|
|
342
|
+
let accountId = null;
|
|
343
|
+
let customUserId = null;
|
|
342
344
|
try {
|
|
343
345
|
await loginWithSdk({
|
|
344
346
|
...options,
|
|
@@ -347,6 +349,10 @@ var loginPlugin = ({ context }) => {
|
|
|
347
349
|
authClientId: context.options?.authClientId
|
|
348
350
|
});
|
|
349
351
|
success = true;
|
|
352
|
+
try {
|
|
353
|
+
({ accountId, customUserId } = await getLoggedInUser());
|
|
354
|
+
} catch {
|
|
355
|
+
}
|
|
350
356
|
} catch (error) {
|
|
351
357
|
success = false;
|
|
352
358
|
errorMessage = error instanceof Error ? error.message : "Login failed";
|
|
@@ -370,7 +376,9 @@ var loginPlugin = ({ context }) => {
|
|
|
370
376
|
session_id: context.session_id,
|
|
371
377
|
selected_api: context.selected_api,
|
|
372
378
|
app_id: context.app_id,
|
|
373
|
-
app_version_id: context.app_version_id
|
|
379
|
+
app_version_id: context.app_version_id,
|
|
380
|
+
customuser_id: customUserId,
|
|
381
|
+
account_id: accountId
|
|
374
382
|
},
|
|
375
383
|
cliVersion: package_default.version
|
|
376
384
|
});
|
package/dist/package.json
CHANGED
|
@@ -27,6 +27,8 @@ export const loginPlugin = ({ context }) => {
|
|
|
27
27
|
const startTime = Date.now();
|
|
28
28
|
let success = false;
|
|
29
29
|
let errorMessage = null;
|
|
30
|
+
let accountId = null;
|
|
31
|
+
let customUserId = null;
|
|
30
32
|
try {
|
|
31
33
|
await loginWithSdk({
|
|
32
34
|
...options,
|
|
@@ -35,6 +37,13 @@ export const loginPlugin = ({ context }) => {
|
|
|
35
37
|
authClientId: context.options?.authClientId,
|
|
36
38
|
});
|
|
37
39
|
success = true;
|
|
40
|
+
// Extract user IDs after successful login
|
|
41
|
+
try {
|
|
42
|
+
({ accountId, customUserId } = await getLoggedInUser());
|
|
43
|
+
}
|
|
44
|
+
catch {
|
|
45
|
+
// If we can't get user info, continue without it
|
|
46
|
+
}
|
|
38
47
|
}
|
|
39
48
|
catch (error) {
|
|
40
49
|
success = false;
|
|
@@ -62,6 +71,8 @@ export const loginPlugin = ({ context }) => {
|
|
|
62
71
|
selected_api: context.selected_api,
|
|
63
72
|
app_id: context.app_id,
|
|
64
73
|
app_version_id: context.app_version_id,
|
|
74
|
+
customuser_id: customUserId,
|
|
75
|
+
account_id: accountId,
|
|
65
76
|
},
|
|
66
77
|
cliVersion: cliPackageJson.version,
|
|
67
78
|
});
|
|
@@ -14,6 +14,26 @@ export declare class SchemaParameterResolver {
|
|
|
14
14
|
private processFieldItems;
|
|
15
15
|
private getNestedValue;
|
|
16
16
|
private setNestedValue;
|
|
17
|
+
/**
|
|
18
|
+
* Extract and normalize field metadata from raw field object
|
|
19
|
+
*/
|
|
20
|
+
private extractFieldMetadata;
|
|
21
|
+
/**
|
|
22
|
+
* Fetch a page of choices for a dropdown field
|
|
23
|
+
*/
|
|
24
|
+
private fetchChoices;
|
|
25
|
+
/**
|
|
26
|
+
* Prompt user with choices (handles both single and multi-select with pagination)
|
|
27
|
+
*/
|
|
28
|
+
private promptWithChoices;
|
|
29
|
+
/**
|
|
30
|
+
* Prompt user for free-form input (text or boolean)
|
|
31
|
+
*/
|
|
32
|
+
private promptFreeForm;
|
|
33
|
+
/**
|
|
34
|
+
* Store field value in inputs object with validation
|
|
35
|
+
*/
|
|
36
|
+
private storeFieldValue;
|
|
17
37
|
private promptForField;
|
|
18
38
|
private isUserCancellation;
|
|
19
39
|
private hasResolver;
|
|
@@ -324,7 +324,7 @@ export class SchemaParameterResolver {
|
|
|
324
324
|
break;
|
|
325
325
|
}
|
|
326
326
|
// Process fields recursively, maintaining fieldset structure
|
|
327
|
-
const fieldStats = await this.processFieldItems(rootFieldItems, inputs, processedFieldKeys, [], iteration);
|
|
327
|
+
const fieldStats = await this.processFieldItems(rootFieldItems, inputs, processedFieldKeys, [], iteration, updatedContext);
|
|
328
328
|
// If no new fields were processed, we're done
|
|
329
329
|
if (fieldStats.newRequired === 0 && fieldStats.newOptional === 0) {
|
|
330
330
|
break;
|
|
@@ -343,7 +343,7 @@ export class SchemaParameterResolver {
|
|
|
343
343
|
* Recursively processes fieldsets and their fields, maintaining natural structure
|
|
344
344
|
* and creating nested inputs as needed (e.g., fieldset "foo" becomes inputs.foo = [{}])
|
|
345
345
|
*/
|
|
346
|
-
async processFieldItems(items, targetInputs, processedFieldKeys, fieldsetPath = [], iteration = 1) {
|
|
346
|
+
async processFieldItems(items, targetInputs, processedFieldKeys, fieldsetPath = [], iteration = 1, context) {
|
|
347
347
|
let newRequiredCount = 0;
|
|
348
348
|
let newOptionalCount = 0;
|
|
349
349
|
let optionalSkipped = false;
|
|
@@ -361,7 +361,7 @@ export class SchemaParameterResolver {
|
|
|
361
361
|
// Process fields within this fieldset recursively
|
|
362
362
|
const fieldsetTarget = targetInputs[typedItem.key][0];
|
|
363
363
|
const nestedPath = [...fieldsetPath, fieldsetTitle];
|
|
364
|
-
const nestedStats = await this.processFieldItems(typedItem.fields, fieldsetTarget, processedFieldKeys, nestedPath, iteration);
|
|
364
|
+
const nestedStats = await this.processFieldItems(typedItem.fields, fieldsetTarget, processedFieldKeys, nestedPath, iteration, context);
|
|
365
365
|
newRequiredCount += nestedStats.newRequired;
|
|
366
366
|
newOptionalCount += nestedStats.newOptional;
|
|
367
367
|
if (nestedStats.optionalSkipped) {
|
|
@@ -381,7 +381,7 @@ export class SchemaParameterResolver {
|
|
|
381
381
|
// Only show this message once at root level
|
|
382
382
|
console.log(chalk.blue(`\n📝 Please provide values for the following ${iteration === 1 ? "" : "additional "}input fields:`));
|
|
383
383
|
}
|
|
384
|
-
await this.promptForField(typedItem, targetInputs);
|
|
384
|
+
await this.promptForField(typedItem, targetInputs, context);
|
|
385
385
|
processedFieldKeys.add(typedItem.key);
|
|
386
386
|
}
|
|
387
387
|
else {
|
|
@@ -415,7 +415,7 @@ export class SchemaParameterResolver {
|
|
|
415
415
|
if (shouldConfigureOptional.configure) {
|
|
416
416
|
console.log(chalk.cyan(`\nOptional fields${pathContext}:`));
|
|
417
417
|
for (const field of optionalFields) {
|
|
418
|
-
await this.promptForField(field, targetInputs);
|
|
418
|
+
await this.promptForField(field, targetInputs, context);
|
|
419
419
|
const typedField = field;
|
|
420
420
|
processedFieldKeys.add(typedField.key);
|
|
421
421
|
}
|
|
@@ -458,36 +458,169 @@ export class SchemaParameterResolver {
|
|
|
458
458
|
}, obj);
|
|
459
459
|
parent[lastKey] = value;
|
|
460
460
|
}
|
|
461
|
-
|
|
461
|
+
/**
|
|
462
|
+
* Extract and normalize field metadata from raw field object
|
|
463
|
+
*/
|
|
464
|
+
extractFieldMetadata(field) {
|
|
462
465
|
const fieldObj = field;
|
|
463
|
-
const
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
466
|
+
const valueType = fieldObj.value_type || "string";
|
|
467
|
+
return {
|
|
468
|
+
key: fieldObj.key,
|
|
469
|
+
title: fieldObj.title || fieldObj.label || fieldObj.key,
|
|
470
|
+
description: fieldObj.description || fieldObj.helpText,
|
|
471
|
+
isRequired: fieldObj.is_required || false,
|
|
472
|
+
defaultValue: fieldObj.default_value ?? fieldObj.default,
|
|
473
|
+
valueType,
|
|
474
|
+
hasDropdown: fieldObj.format === "SELECT",
|
|
475
|
+
isMultiSelect: Boolean(valueType === "array" ||
|
|
476
|
+
(fieldObj.items && fieldObj.items.type !== undefined)),
|
|
467
477
|
};
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
478
|
+
}
|
|
479
|
+
/**
|
|
480
|
+
* Fetch a page of choices for a dropdown field
|
|
481
|
+
*/
|
|
482
|
+
async fetchChoices(fieldMeta, inputs, context, cursor) {
|
|
483
|
+
try {
|
|
484
|
+
console.log(chalk.gray(cursor
|
|
485
|
+
? ` Fetching more choices...`
|
|
486
|
+
: ` Fetching choices for ${fieldMeta.title}...`));
|
|
487
|
+
const page = await context.sdk.listInputFieldChoices({
|
|
488
|
+
appKey: context.resolvedParams.appKey,
|
|
489
|
+
actionKey: context.resolvedParams.actionKey,
|
|
490
|
+
actionType: context.resolvedParams.actionType,
|
|
491
|
+
authenticationId: context.resolvedParams.authenticationId,
|
|
492
|
+
inputFieldKey: fieldMeta.key,
|
|
493
|
+
inputs,
|
|
494
|
+
...(cursor && { cursor }),
|
|
482
495
|
});
|
|
496
|
+
const choices = page.data.map((choice) => ({
|
|
497
|
+
label: choice.label || choice.key || String(choice.value),
|
|
498
|
+
value: choice.value ?? choice.key,
|
|
499
|
+
}));
|
|
500
|
+
if (choices.length === 0 && !cursor) {
|
|
501
|
+
console.log(chalk.yellow(` No choices available for ${fieldMeta.title}`));
|
|
502
|
+
}
|
|
503
|
+
return {
|
|
504
|
+
choices,
|
|
505
|
+
nextCursor: page.nextCursor,
|
|
506
|
+
};
|
|
507
|
+
}
|
|
508
|
+
catch (error) {
|
|
509
|
+
console.warn(chalk.yellow(` ⚠️ Failed to fetch choices for ${fieldMeta.title}:`), error);
|
|
510
|
+
return { choices: [] };
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
/**
|
|
514
|
+
* Prompt user with choices (handles both single and multi-select with pagination)
|
|
515
|
+
*/
|
|
516
|
+
async promptWithChoices({ fieldMeta, choices: initialChoices, nextCursor: initialCursor, inputs, context, }) {
|
|
517
|
+
const choices = [...initialChoices];
|
|
518
|
+
let nextCursor = initialCursor;
|
|
519
|
+
const LOAD_MORE_SENTINEL = Symbol("LOAD_MORE");
|
|
520
|
+
// Progressive loading loop
|
|
521
|
+
while (true) {
|
|
522
|
+
const promptChoices = choices.map((choice) => ({
|
|
523
|
+
name: choice.label,
|
|
524
|
+
value: choice.value,
|
|
525
|
+
}));
|
|
526
|
+
// Add "(Load more...)" option if there are more pages
|
|
527
|
+
if (nextCursor) {
|
|
528
|
+
promptChoices.push({
|
|
529
|
+
name: chalk.dim("(Load more...)"),
|
|
530
|
+
value: LOAD_MORE_SENTINEL,
|
|
531
|
+
});
|
|
532
|
+
}
|
|
533
|
+
// Add skip option for optional fields (single-select only)
|
|
534
|
+
if (!fieldMeta.isRequired && !fieldMeta.isMultiSelect) {
|
|
535
|
+
promptChoices.push({ name: "(Skip)", value: undefined });
|
|
536
|
+
}
|
|
537
|
+
const promptConfig = {
|
|
538
|
+
type: fieldMeta.isMultiSelect ? "checkbox" : "list",
|
|
539
|
+
name: fieldMeta.key,
|
|
540
|
+
message: `${fieldMeta.title}${fieldMeta.isRequired ? " (required)" : " (optional)"}:`,
|
|
541
|
+
choices: promptChoices,
|
|
542
|
+
...(fieldMeta.isMultiSelect && {
|
|
543
|
+
validate: (input) => {
|
|
544
|
+
if (fieldMeta.isRequired && (!input || input.length === 0)) {
|
|
545
|
+
return "At least one selection is required";
|
|
546
|
+
}
|
|
547
|
+
return true;
|
|
548
|
+
},
|
|
549
|
+
}),
|
|
550
|
+
};
|
|
551
|
+
const answer = await inquirer.prompt([promptConfig]);
|
|
552
|
+
let selectedValue = answer[fieldMeta.key];
|
|
553
|
+
// Check if user selected "Load more..."
|
|
554
|
+
const wantsMore = fieldMeta.isMultiSelect
|
|
555
|
+
? Array.isArray(selectedValue) &&
|
|
556
|
+
selectedValue.includes(LOAD_MORE_SENTINEL)
|
|
557
|
+
: selectedValue === LOAD_MORE_SENTINEL;
|
|
558
|
+
if (wantsMore && nextCursor && context) {
|
|
559
|
+
// Remove sentinel from multi-select
|
|
560
|
+
if (fieldMeta.isMultiSelect && Array.isArray(selectedValue)) {
|
|
561
|
+
selectedValue = selectedValue.filter((v) => v !== LOAD_MORE_SENTINEL);
|
|
562
|
+
}
|
|
563
|
+
// Fetch next page
|
|
564
|
+
const result = await this.fetchChoices(fieldMeta, inputs, context, nextCursor);
|
|
565
|
+
choices.push(...result.choices);
|
|
566
|
+
nextCursor = result.nextCursor;
|
|
567
|
+
// Re-prompt with updated choices
|
|
568
|
+
continue;
|
|
569
|
+
}
|
|
570
|
+
return selectedValue;
|
|
571
|
+
}
|
|
572
|
+
}
|
|
573
|
+
/**
|
|
574
|
+
* Prompt user for free-form input (text or boolean)
|
|
575
|
+
*/
|
|
576
|
+
async promptFreeForm(fieldMeta) {
|
|
577
|
+
const promptConfig = {
|
|
578
|
+
name: fieldMeta.key,
|
|
579
|
+
message: `${fieldMeta.title}${fieldMeta.isRequired ? " (required)" : " (optional)"}:`,
|
|
580
|
+
};
|
|
581
|
+
if (fieldMeta.valueType === "boolean") {
|
|
582
|
+
promptConfig.type = "confirm";
|
|
583
|
+
promptConfig.default =
|
|
584
|
+
fieldMeta.defaultValue !== undefined
|
|
585
|
+
? Boolean(fieldMeta.defaultValue)
|
|
586
|
+
: undefined;
|
|
587
|
+
}
|
|
588
|
+
else {
|
|
589
|
+
promptConfig.type = "input";
|
|
590
|
+
promptConfig.default = fieldMeta.defaultValue;
|
|
591
|
+
promptConfig.validate = (input) => {
|
|
592
|
+
if (fieldMeta.isRequired && !input) {
|
|
593
|
+
return "This field is required";
|
|
594
|
+
}
|
|
595
|
+
return true;
|
|
596
|
+
};
|
|
597
|
+
}
|
|
598
|
+
// Add help text if available
|
|
599
|
+
if (fieldMeta.description) {
|
|
600
|
+
promptConfig.prefix = chalk.gray(`ℹ ${fieldMeta.description}\n`);
|
|
483
601
|
}
|
|
484
602
|
try {
|
|
485
|
-
const answer = await inquirer.prompt([
|
|
486
|
-
|
|
487
|
-
|
|
603
|
+
const answer = await inquirer.prompt([promptConfig]);
|
|
604
|
+
return answer[fieldMeta.key];
|
|
605
|
+
}
|
|
606
|
+
catch (error) {
|
|
607
|
+
if (this.isUserCancellation(error)) {
|
|
608
|
+
console.log(chalk.yellow("\n\nOperation cancelled by user"));
|
|
609
|
+
throw new ZapierCliUserCancellationError();
|
|
488
610
|
}
|
|
489
|
-
|
|
490
|
-
|
|
611
|
+
throw error;
|
|
612
|
+
}
|
|
613
|
+
}
|
|
614
|
+
/**
|
|
615
|
+
* Store field value in inputs object with validation
|
|
616
|
+
*/
|
|
617
|
+
storeFieldValue(inputs, key, value, isRequired) {
|
|
618
|
+
try {
|
|
619
|
+
if (value !== undefined && value !== "") {
|
|
620
|
+
inputs[key] = value;
|
|
621
|
+
}
|
|
622
|
+
else if (isRequired) {
|
|
623
|
+
throw new Error(`Required field ${key} cannot be empty`);
|
|
491
624
|
}
|
|
492
625
|
}
|
|
493
626
|
catch (error) {
|
|
@@ -498,6 +631,33 @@ export class SchemaParameterResolver {
|
|
|
498
631
|
throw error;
|
|
499
632
|
}
|
|
500
633
|
}
|
|
634
|
+
async promptForField(field, inputs, context) {
|
|
635
|
+
const fieldMeta = this.extractFieldMetadata(field);
|
|
636
|
+
// Fetch choices if field has dropdown
|
|
637
|
+
let choices = [];
|
|
638
|
+
let nextCursor;
|
|
639
|
+
if (fieldMeta.hasDropdown && context) {
|
|
640
|
+
const result = await this.fetchChoices(fieldMeta, inputs, context);
|
|
641
|
+
choices = result.choices;
|
|
642
|
+
nextCursor = result.nextCursor;
|
|
643
|
+
}
|
|
644
|
+
// Prompt user based on field type
|
|
645
|
+
let selectedValue;
|
|
646
|
+
if (choices.length > 0) {
|
|
647
|
+
selectedValue = await this.promptWithChoices({
|
|
648
|
+
fieldMeta,
|
|
649
|
+
choices,
|
|
650
|
+
nextCursor,
|
|
651
|
+
inputs,
|
|
652
|
+
context,
|
|
653
|
+
});
|
|
654
|
+
}
|
|
655
|
+
else {
|
|
656
|
+
selectedValue = await this.promptFreeForm(fieldMeta);
|
|
657
|
+
}
|
|
658
|
+
// Store result
|
|
659
|
+
this.storeFieldValue(inputs, fieldMeta.key, selectedValue, fieldMeta.isRequired);
|
|
660
|
+
}
|
|
501
661
|
isUserCancellation(error) {
|
|
502
662
|
const errorObj = error;
|
|
503
663
|
return (errorObj?.name === "ExitPromptError" ||
|