@zapier/zapier-sdk-cli 0.35.1 → 0.36.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.mjs CHANGED
@@ -4,14 +4,15 @@ import { z } from 'zod';
4
4
  import { createFunction, OutputPropertySchema, DEFAULT_CONFIG_PATH, injectCliLogin, createZapierSdkWithoutRegistry, registryPlugin, ZapierError, ZapierValidationError, ZapierUnknownError, batch, toSnakeCase, buildApplicationLifecycleEvent, isCredentialsObject, isPositional, runWithTelemetryContext, formatErrorMessage, getOsInfo, getPlatformVersions, getCiPlatform, isCi, getReleaseId, getCurrentTimestamp, generateEventId } from '@zapier/zapier-sdk';
5
5
  import inquirer from 'inquirer';
6
6
  import chalk7 from 'chalk';
7
+ import ora from 'ora';
7
8
  import util from 'util';
9
+ import wrapAnsi from 'wrap-ansi';
8
10
  import * as cliLogin from '@zapier/zapier-sdk-cli-login';
9
11
  import { logout, getConfigPath, getLoggedInUser, getPkceLoginConfig, AUTH_MODE_HEADER, getLoginStorageMode, updateLogin, getConfig } from '@zapier/zapier-sdk-cli-login';
10
12
  import open from 'open';
11
13
  import crypto, { createHash } from 'crypto';
12
14
  import express from 'express';
13
15
  import pkceChallenge from 'pkce-challenge';
14
- import ora from 'ora';
15
16
  import { startMcpServer } from '@zapier/zapier-sdk-mcp';
16
17
  import { buildSync } from 'esbuild';
17
18
  import * as fs from 'fs';
@@ -106,9 +107,31 @@ function getLocalResolutionOrderForParams(paramNames, resolvers) {
106
107
  return order;
107
108
  }
108
109
  var SchemaParameterResolver = class {
110
+ constructor() {
111
+ this.debug = false;
112
+ this.spinner = null;
113
+ }
114
+ debugLog(message) {
115
+ if (this.debug) {
116
+ this.stopSpinner();
117
+ console.log(chalk7.gray(`[Zapier CLI] ${message}`));
118
+ }
119
+ }
120
+ startSpinner() {
121
+ if (!this.debug && !this.spinner) {
122
+ this.spinner = ora({ text: "", spinner: "dots" }).start();
123
+ }
124
+ }
125
+ stopSpinner() {
126
+ if (this.spinner) {
127
+ this.spinner.stop();
128
+ this.spinner = null;
129
+ }
130
+ }
109
131
  async resolveParameters(schema, providedParams, sdk2, functionName, options) {
110
132
  return runWithTelemetryContext(async () => {
111
- const interactiveMode = options?.interactiveMode ?? true;
133
+ this.debug = options?.debug ?? false;
134
+ const interactiveMode = (options?.interactiveMode ?? true) && !!process.stdin.isTTY;
112
135
  const parseResult = schema.safeParse(providedParams);
113
136
  const allParams = this.extractParametersFromSchema(schema);
114
137
  const resolvableParams = allParams.filter(
@@ -118,27 +141,18 @@ var SchemaParameterResolver = class {
118
141
  const hasValue = this.getNestedValue(providedParams, param.path) !== void 0;
119
142
  return !hasValue;
120
143
  });
121
- const functionallyRequired = missingResolvable.filter((param) => {
144
+ const required = missingResolvable.filter((param) => {
122
145
  if (param.isRequired) return true;
123
- if (param.name === "inputs") {
124
- return interactiveMode;
125
- }
146
+ if (param.name === "inputs") return interactiveMode;
126
147
  return false;
127
148
  });
128
- const alwaysPrompt = missingResolvable.filter((param) => {
129
- if (functionallyRequired.includes(param)) return false;
130
- if (param.name === "connectionId") {
131
- return true;
132
- }
133
- return false;
134
- });
135
- const trulyOptional = missingResolvable.filter(
136
- (param) => !functionallyRequired.includes(param) && !alwaysPrompt.includes(param)
149
+ const optional = missingResolvable.filter(
150
+ (param) => !required.includes(param)
137
151
  );
138
- if (parseResult.success && functionallyRequired.length === 0 && alwaysPrompt.length === 0) {
152
+ if (parseResult.success && required.length === 0 && optional.length === 0) {
139
153
  return parseResult.data;
140
154
  }
141
- if (functionallyRequired.length === 0 && alwaysPrompt.length === 0) {
155
+ if (required.length === 0 && optional.length === 0) {
142
156
  if (!parseResult.success) {
143
157
  throw new ZapierCliValidationError(formatZodError(parseResult.error));
144
158
  }
@@ -152,19 +166,16 @@ var SchemaParameterResolver = class {
152
166
  functionName
153
167
  };
154
168
  const localResolvers = this.getLocalResolvers(sdk2, functionName);
155
- if (functionallyRequired.length > 0) {
156
- const requiredParamNames = functionallyRequired.map((p) => p.name);
169
+ if (required.length > 0) {
170
+ const requiredParamNames = required.map((p) => p.name);
157
171
  const requiredResolutionOrder = getLocalResolutionOrderForParams(
158
172
  requiredParamNames,
159
173
  localResolvers
160
174
  );
161
175
  const orderedRequiredParams = requiredResolutionOrder.map((paramName) => {
162
- let param = functionallyRequired.find((p) => p.name === paramName);
176
+ let param = required.find((p) => p.name === paramName);
163
177
  if (!param) {
164
- param = alwaysPrompt.find((p) => p.name === paramName);
165
- }
166
- if (!param) {
167
- param = trulyOptional.find((p) => p.name === paramName);
178
+ param = optional.find((p) => p.name === paramName);
168
179
  }
169
180
  return param;
170
181
  }).filter((param) => param !== void 0);
@@ -197,33 +208,31 @@ var SchemaParameterResolver = class {
197
208
  const resolvedParamNames = new Set(
198
209
  orderedRequiredParams.map((p) => p.name)
199
210
  );
200
- alwaysPrompt.splice(
201
- 0,
202
- alwaysPrompt.length,
203
- ...alwaysPrompt.filter((p) => !resolvedParamNames.has(p.name))
204
- );
205
- trulyOptional.splice(
211
+ optional.splice(
206
212
  0,
207
- trulyOptional.length,
208
- ...trulyOptional.filter((p) => !resolvedParamNames.has(p.name))
213
+ optional.length,
214
+ ...optional.filter((p) => !resolvedParamNames.has(p.name))
209
215
  );
210
216
  }
211
- if (interactiveMode && alwaysPrompt.length > 0) {
212
- const alwaysPromptNames = alwaysPrompt.map((p) => p.name);
213
- const alwaysPromptResolutionOrder = getLocalResolutionOrderForParams(
214
- alwaysPromptNames,
217
+ if (interactiveMode && optional.length > 0) {
218
+ const optionalParamNames = optional.map((p) => p.name);
219
+ const optionalResolutionOrder = getLocalResolutionOrderForParams(
220
+ optionalParamNames,
215
221
  localResolvers
216
222
  );
217
- const orderedAlwaysPromptParams = alwaysPromptResolutionOrder.map((paramName) => alwaysPrompt.find((p) => p.name === paramName)).filter((param) => param !== void 0);
218
- for (const param of orderedAlwaysPromptParams) {
223
+ const orderedOptionalParams = optionalResolutionOrder.map((paramName) => optional.find((p) => p.name === paramName)).filter((param) => param !== void 0);
224
+ for (const param of orderedOptionalParams) {
219
225
  try {
220
226
  const value = await this.resolveParameter(
221
227
  param,
222
228
  context,
223
- functionName
229
+ functionName,
230
+ { isOptional: true }
224
231
  );
225
- this.setNestedValue(resolvedParams, param.path, value);
226
- context.resolvedParams = resolvedParams;
232
+ if (value !== void 0) {
233
+ this.setNestedValue(resolvedParams, param.path, value);
234
+ context.resolvedParams = resolvedParams;
235
+ }
227
236
  } catch (error) {
228
237
  if (this.isUserCancellation(error)) {
229
238
  console.log(chalk7.yellow("\n\nOperation cancelled by user"));
@@ -233,44 +242,6 @@ var SchemaParameterResolver = class {
233
242
  }
234
243
  }
235
244
  }
236
- if (interactiveMode && trulyOptional.length > 0) {
237
- const optionalNames = trulyOptional.map((p) => p.name).join(", ");
238
- const shouldResolveOptional = await inquirer.prompt([
239
- {
240
- type: "confirm",
241
- name: "resolveOptional",
242
- message: `Would you like to be prompted for optional parameters (${optionalNames})?`,
243
- default: false
244
- }
245
- ]);
246
- if (shouldResolveOptional.resolveOptional) {
247
- const optionalParamNames = trulyOptional.map((p) => p.name);
248
- const optionalResolutionOrder = getLocalResolutionOrderForParams(
249
- optionalParamNames,
250
- localResolvers
251
- );
252
- const orderedOptionalParams = optionalResolutionOrder.map((paramName) => trulyOptional.find((p) => p.name === paramName)).filter(
253
- (param) => param !== void 0
254
- );
255
- for (const param of orderedOptionalParams) {
256
- try {
257
- const value = await this.resolveParameter(
258
- param,
259
- context,
260
- functionName
261
- );
262
- this.setNestedValue(resolvedParams, param.path, value);
263
- context.resolvedParams = resolvedParams;
264
- } catch (error) {
265
- if (this.isUserCancellation(error)) {
266
- console.log(chalk7.yellow("\n\nOperation cancelled by user"));
267
- throw new ZapierCliUserCancellationError();
268
- }
269
- throw error;
270
- }
271
- }
272
- }
273
- }
274
245
  const finalResult = schema.safeParse(resolvedParams);
275
246
  if (!finalResult.success) {
276
247
  throw new ZapierCliValidationError(
@@ -365,7 +336,7 @@ var SchemaParameterResolver = class {
365
336
  throw new ZapierCliMissingParametersError(missingParams);
366
337
  }
367
338
  }
368
- async resolveParameter(param, context, functionName) {
339
+ async resolveParameter(param, context, functionName, options) {
369
340
  const resolver = this.getResolver(
370
341
  param.name,
371
342
  context.sdk,
@@ -374,53 +345,102 @@ var SchemaParameterResolver = class {
374
345
  if (!resolver) {
375
346
  throw new Error(`No resolver found for parameter: ${param.name}`);
376
347
  }
377
- console.log(chalk7.blue(`
378
- \u{1F50D} Resolving ${param.name}...`));
348
+ return this.resolveWithResolver(resolver, param, context, {
349
+ isOptional: options?.isOptional
350
+ });
351
+ }
352
+ async resolveWithResolver(resolver, param, context, options = {}) {
353
+ const { arrayIndex, isOptional } = options;
354
+ const inArrayContext = arrayIndex != null;
355
+ const promptLabel = inArrayContext ? `${param.name}[${arrayIndex}]` : param.name;
356
+ const promptName = inArrayContext ? "value" : param.name;
357
+ this.debugLog(`Resolving ${promptLabel}${isOptional ? " (optional)" : ""}`);
379
358
  if (resolver.type === "static") {
380
359
  const staticResolver = resolver;
381
360
  const promptConfig = {
382
361
  type: staticResolver.inputType === "password" ? "password" : "input",
383
- name: param.name,
384
- message: `Enter ${param.name}:`,
362
+ name: promptName,
363
+ message: `Enter ${promptLabel}${isOptional ? " (optional)" : ""}:`,
385
364
  ...staticResolver.placeholder && {
386
365
  default: staticResolver.placeholder
387
366
  }
388
367
  };
368
+ this.stopSpinner();
389
369
  const answers = await inquirer.prompt([promptConfig]);
390
- return answers[param.name];
370
+ const value = answers[promptName];
371
+ if (isOptional && (value === void 0 || value === "")) {
372
+ return void 0;
373
+ }
374
+ return value;
391
375
  } else if (resolver.type === "dynamic") {
392
376
  const dynamicResolver = resolver;
377
+ this.startSpinner();
393
378
  const autoResolution = await this.tryAutoResolve(
394
379
  dynamicResolver,
395
380
  context
396
381
  );
397
382
  if (autoResolution != null) {
383
+ this.stopSpinner();
398
384
  return autoResolution.resolvedValue;
399
385
  }
400
- if (param.isRequired && param.name !== "connectionId") {
401
- console.log(chalk7.gray(`Fetching options for ${param.name}...`));
402
- }
403
- const items = await dynamicResolver.fetch(
386
+ this.debugLog(`Fetching options for ${promptLabel}`);
387
+ const fetchResult = await dynamicResolver.fetch(
404
388
  context.sdk,
405
389
  context.resolvedParams
406
390
  );
407
- const safeItems = items || [];
391
+ const items = Array.isArray(fetchResult) ? fetchResult : fetchResult?.data ?? [];
408
392
  const promptConfig = dynamicResolver.prompt(
409
- safeItems,
393
+ items,
410
394
  context.resolvedParams
411
395
  );
396
+ promptConfig.name = promptName;
397
+ this.stopSpinner();
398
+ if (isOptional && promptConfig.choices) {
399
+ const SKIP_SENTINEL = Symbol("SKIP");
400
+ promptConfig.choices = [
401
+ { name: chalk7.dim("(Skip)"), value: SKIP_SENTINEL },
402
+ ...promptConfig.choices
403
+ ];
404
+ const answers2 = await inquirer.prompt([promptConfig]);
405
+ const value = answers2[promptName];
406
+ if (value === SKIP_SENTINEL) {
407
+ return void 0;
408
+ }
409
+ return value;
410
+ }
412
411
  const answers = await inquirer.prompt([promptConfig]);
413
- return answers[param.name];
412
+ return answers[promptName];
414
413
  } else if (resolver.type === "fields") {
414
+ if (isOptional && !inArrayContext) {
415
+ this.stopSpinner();
416
+ const { confirm } = await inquirer.prompt([
417
+ {
418
+ type: "confirm",
419
+ name: "confirm",
420
+ message: `Add ${promptLabel}?`,
421
+ default: false
422
+ }
423
+ ]);
424
+ if (!confirm) {
425
+ return void 0;
426
+ }
427
+ }
415
428
  return await this.resolveFieldsRecursively(
429
+ resolver,
430
+ context,
431
+ param,
432
+ { inArrayContext }
433
+ );
434
+ } else if (resolver.type === "array") {
435
+ return await this.resolveArrayRecursively(
416
436
  resolver,
417
437
  context,
418
438
  param
419
439
  );
420
440
  }
421
- throw new Error(`Unknown resolver type for ${param.name}`);
441
+ throw new Error(`Unknown resolver type for ${promptLabel}`);
422
442
  }
423
- async resolveFieldsRecursively(resolver, context, param) {
443
+ async resolveFieldsRecursively(resolver, context, param, options = {}) {
424
444
  const inputs = {};
425
445
  let processedFieldKeys = /* @__PURE__ */ new Set();
426
446
  let iteration = 0;
@@ -434,15 +454,15 @@ var SchemaParameterResolver = class {
434
454
  inputs
435
455
  }
436
456
  };
437
- console.log(
438
- chalk7.gray(
439
- `Fetching input fields for ${param.name}${iteration > 1 ? ` (iteration ${iteration})` : ""}...`
440
- )
457
+ this.debugLog(
458
+ `Fetching input fields for ${param.name}${iteration > 1 ? ` (iteration ${iteration})` : ""}`
441
459
  );
460
+ this.startSpinner();
442
461
  const rootFieldItems = await resolver.fetch(
443
462
  updatedContext.sdk,
444
463
  updatedContext.resolvedParams
445
464
  );
465
+ this.stopSpinner();
446
466
  if (!rootFieldItems || rootFieldItems.length === 0) {
447
467
  if (iteration === 1) {
448
468
  console.log(
@@ -457,7 +477,8 @@ var SchemaParameterResolver = class {
457
477
  processedFieldKeys,
458
478
  [],
459
479
  iteration,
460
- updatedContext
480
+ updatedContext,
481
+ { inArrayContext: options.inArrayContext }
461
482
  );
462
483
  if (fieldStats.newRequired === 0 && fieldStats.newOptional === 0) {
463
484
  break;
@@ -474,13 +495,60 @@ var SchemaParameterResolver = class {
474
495
  )
475
496
  );
476
497
  }
498
+ if (resolver.transform) {
499
+ return resolver.transform(inputs);
500
+ }
477
501
  return inputs;
478
502
  }
503
+ /**
504
+ * Resolves an array parameter by repeatedly prompting for items until user says no
505
+ */
506
+ async resolveArrayRecursively(resolver, context, param) {
507
+ const items = [];
508
+ const minItems = resolver.minItems ?? 0;
509
+ const maxItems = resolver.maxItems ?? Infinity;
510
+ while (items.length < maxItems) {
511
+ const currentIndex = items.length;
512
+ if (currentIndex >= minItems) {
513
+ this.stopSpinner();
514
+ const confirmAnswer = await inquirer.prompt([
515
+ {
516
+ type: "confirm",
517
+ name: "addItem",
518
+ message: `Add ${param.name}[${currentIndex}]?`,
519
+ default: false
520
+ }
521
+ ]);
522
+ if (!confirmAnswer.addItem) {
523
+ break;
524
+ }
525
+ }
526
+ const innerResolver = await resolver.fetch(
527
+ context.sdk,
528
+ context.resolvedParams
529
+ );
530
+ const itemValue = await this.resolveWithResolver(
531
+ innerResolver,
532
+ param,
533
+ context,
534
+ { arrayIndex: currentIndex }
535
+ );
536
+ items.push(itemValue);
537
+ context.resolvedParams = {
538
+ ...context.resolvedParams,
539
+ [param.name]: items
540
+ };
541
+ }
542
+ if (items.length >= maxItems) {
543
+ console.log(chalk7.gray(`Maximum of ${maxItems} items reached.`));
544
+ }
545
+ return items;
546
+ }
479
547
  /**
480
548
  * Recursively processes fieldsets and their fields, maintaining natural structure
481
549
  * and creating nested inputs as needed (e.g., fieldset "foo" becomes inputs.foo = [{}])
482
550
  */
483
- async processFieldItems(items, targetInputs, processedFieldKeys, fieldsetPath = [], iteration = 1, context) {
551
+ async processFieldItems(items, targetInputs, processedFieldKeys, fieldsetPath = [], iteration = 1, context, options = {}) {
484
552
  let newRequiredCount = 0;
485
553
  let newOptionalCount = 0;
486
554
  let optionalSkipped = false;
@@ -506,7 +574,8 @@ var SchemaParameterResolver = class {
506
574
  processedFieldKeys,
507
575
  nestedPath,
508
576
  iteration,
509
- context
577
+ context,
578
+ options
510
579
  );
511
580
  newRequiredCount += nestedStats.newRequired;
512
581
  newOptionalCount += nestedStats.newOptional;
@@ -520,15 +589,22 @@ var SchemaParameterResolver = class {
520
589
  const isRequired = typedItem.is_required || false;
521
590
  if (isRequired) {
522
591
  newRequiredCount++;
523
- if (newRequiredCount === 1 && fieldsetPath.length === 0) {
524
- console.log(
525
- chalk7.blue(
526
- `
527
- \u{1F4DD} Please provide values for the following ${iteration === 1 ? "" : "additional "}input fields:`
528
- )
592
+ if (typedItem.resolver && context) {
593
+ const param = {
594
+ name: typedItem.key,
595
+ path: [typedItem.key],
596
+ schema: z.unknown(),
597
+ isRequired: true
598
+ };
599
+ targetInputs[typedItem.key] = await this.resolveWithResolver(
600
+ typedItem.resolver,
601
+ param,
602
+ context,
603
+ { isOptional: false }
529
604
  );
605
+ } else {
606
+ await this.promptForField(typedItem, targetInputs, context);
530
607
  }
531
- await this.promptForField(typedItem, targetInputs, context);
532
608
  processedFieldKeys.add(typedItem.key);
533
609
  } else {
534
610
  newOptionalCount++;
@@ -542,42 +618,50 @@ var SchemaParameterResolver = class {
542
618
  });
543
619
  if (optionalFields.length > 0) {
544
620
  const pathContext = fieldsetPath.length > 0 ? ` in ${fieldsetPath.join(" > ")}` : "";
545
- console.log(
546
- chalk7.gray(
547
- `
621
+ if (options.inArrayContext) {
622
+ for (const field of optionalFields) {
623
+ await this.promptForField(field, targetInputs, context);
624
+ const typedField = field;
625
+ processedFieldKeys.add(typedField.key);
626
+ }
627
+ } else {
628
+ console.log(
629
+ chalk7.gray(
630
+ `
548
631
  There are ${optionalFields.length} ${iteration === 1 ? "" : "additional "}optional field(s) available${pathContext}.`
549
- )
550
- );
551
- try {
552
- const shouldConfigureOptional = await inquirer.prompt([
553
- {
554
- type: "confirm",
555
- name: "configure",
556
- message: `Would you like to configure ${iteration === 1 ? "" : "these additional "}optional fields${pathContext}?`,
557
- default: false
558
- }
559
- ]);
560
- if (shouldConfigureOptional.configure) {
561
- console.log(chalk7.cyan(`
632
+ )
633
+ );
634
+ try {
635
+ const shouldConfigureOptional = await inquirer.prompt([
636
+ {
637
+ type: "confirm",
638
+ name: "configure",
639
+ message: `Would you like to configure ${iteration === 1 ? "" : "these additional "}optional fields${pathContext}?`,
640
+ default: false
641
+ }
642
+ ]);
643
+ if (shouldConfigureOptional.configure) {
644
+ console.log(chalk7.cyan(`
562
645
  Optional fields${pathContext}:`));
563
- for (const field of optionalFields) {
564
- await this.promptForField(field, targetInputs, context);
565
- const typedField = field;
566
- processedFieldKeys.add(typedField.key);
646
+ for (const field of optionalFields) {
647
+ await this.promptForField(field, targetInputs, context);
648
+ const typedField = field;
649
+ processedFieldKeys.add(typedField.key);
650
+ }
651
+ } else {
652
+ optionalSkipped = true;
653
+ optionalFields.forEach((field) => {
654
+ const typedField = field;
655
+ processedFieldKeys.add(typedField.key);
656
+ });
567
657
  }
568
- } else {
569
- optionalSkipped = true;
570
- optionalFields.forEach((field) => {
571
- const typedField = field;
572
- processedFieldKeys.add(typedField.key);
573
- });
574
- }
575
- } catch (error) {
576
- if (this.isUserCancellation(error)) {
577
- console.log(chalk7.yellow("\n\nOperation cancelled by user"));
578
- throw new ZapierCliUserCancellationError();
658
+ } catch (error) {
659
+ if (this.isUserCancellation(error)) {
660
+ console.log(chalk7.yellow("\n\nOperation cancelled by user"));
661
+ throw new ZapierCliUserCancellationError();
662
+ }
663
+ throw error;
579
664
  }
580
- throw error;
581
665
  }
582
666
  }
583
667
  }
@@ -617,10 +701,11 @@ Optional fields${pathContext}:`));
617
701
  isRequired: fieldObj.is_required || false,
618
702
  defaultValue: fieldObj.default_value ?? fieldObj.default,
619
703
  valueType,
620
- hasDropdown: fieldObj.format === "SELECT",
704
+ hasDropdown: fieldObj.format === "SELECT" || Boolean(fieldObj.choices),
621
705
  isMultiSelect: Boolean(
622
706
  valueType === "array" || fieldObj.items && fieldObj.items.type !== void 0
623
- )
707
+ ),
708
+ inlineChoices: fieldObj.choices
624
709
  };
625
710
  }
626
711
  /**
@@ -628,11 +713,10 @@ Optional fields${pathContext}:`));
628
713
  */
629
714
  async fetchChoices(fieldMeta, inputs, context, cursor) {
630
715
  try {
631
- console.log(
632
- chalk7.gray(
633
- cursor ? ` Fetching more choices...` : ` Fetching choices for ${fieldMeta.title}...`
634
- )
716
+ this.debugLog(
717
+ cursor ? `Fetching more choices for ${fieldMeta.title}` : `Fetching choices for ${fieldMeta.title}`
635
718
  );
719
+ this.startSpinner();
636
720
  const page = await context.sdk.listInputFieldChoices({
637
721
  appKey: context.resolvedParams.appKey,
638
722
  actionKey: context.resolvedParams.actionKey,
@@ -642,13 +726,14 @@ Optional fields${pathContext}:`));
642
726
  inputs,
643
727
  ...cursor && { cursor }
644
728
  });
729
+ this.stopSpinner();
645
730
  const choices = page.data.map((choice) => ({
646
731
  label: choice.label || choice.key || String(choice.value),
647
732
  value: choice.value ?? choice.key
648
733
  }));
649
734
  if (choices.length === 0 && !cursor) {
650
735
  console.log(
651
- chalk7.yellow(` No choices available for ${fieldMeta.title}`)
736
+ chalk7.yellow(`No choices available for ${fieldMeta.title}`)
652
737
  );
653
738
  }
654
739
  return {
@@ -656,8 +741,9 @@ Optional fields${pathContext}:`));
656
741
  nextCursor: page.nextCursor
657
742
  };
658
743
  } catch (error) {
744
+ this.stopSpinner();
659
745
  console.warn(
660
- chalk7.yellow(` \u26A0\uFE0F Failed to fetch choices for ${fieldMeta.title}:`),
746
+ chalk7.yellow(`Failed to fetch choices for ${fieldMeta.title}:`),
661
747
  error
662
748
  );
663
749
  return { choices: [] };
@@ -673,6 +759,7 @@ Optional fields${pathContext}:`));
673
759
  inputs,
674
760
  context
675
761
  }) {
762
+ this.stopSpinner();
676
763
  const choices = [...initialChoices];
677
764
  let nextCursor = initialCursor;
678
765
  const LOAD_MORE_SENTINEL = Symbol("LOAD_MORE");
@@ -735,6 +822,27 @@ Optional fields${pathContext}:`));
735
822
  if (fieldMeta.valueType === "boolean") {
736
823
  promptConfig.type = "confirm";
737
824
  promptConfig.default = fieldMeta.defaultValue !== void 0 ? Boolean(fieldMeta.defaultValue) : void 0;
825
+ } else if (fieldMeta.valueType === "array") {
826
+ promptConfig.type = "input";
827
+ promptConfig.default = fieldMeta.defaultValue;
828
+ promptConfig.message = `${fieldMeta.title}${fieldMeta.isRequired ? " (required)" : " (optional)"} (JSON array or comma-separated):`;
829
+ promptConfig.validate = (input) => {
830
+ if (fieldMeta.isRequired && !input) {
831
+ return "This field is required";
832
+ }
833
+ return true;
834
+ };
835
+ promptConfig.filter = (input) => {
836
+ if (!input) return input;
837
+ const trimmed = input.trim();
838
+ if (trimmed.startsWith("[")) {
839
+ try {
840
+ return JSON.parse(trimmed);
841
+ } catch {
842
+ }
843
+ }
844
+ return trimmed.split(",").map((s) => s.trim());
845
+ };
738
846
  } else {
739
847
  promptConfig.type = "input";
740
848
  promptConfig.default = fieldMeta.defaultValue;
@@ -782,7 +890,9 @@ Optional fields${pathContext}:`));
782
890
  const fieldMeta = this.extractFieldMetadata(field);
783
891
  let choices = [];
784
892
  let nextCursor;
785
- if (fieldMeta.hasDropdown && context) {
893
+ if (fieldMeta.inlineChoices) {
894
+ choices = fieldMeta.inlineChoices;
895
+ } else if (fieldMeta.hasDropdown && context) {
786
896
  const result = await this.fetchChoices(fieldMeta, inputs, context);
787
897
  choices = result.choices;
788
898
  nextCursor = result.nextCursor;
@@ -876,7 +986,7 @@ var SHARED_COMMAND_CLI_OPTIONS = [
876
986
 
877
987
  // package.json
878
988
  var package_default = {
879
- version: "0.35.1"};
989
+ version: "0.36.1"};
880
990
 
881
991
  // src/telemetry/builders.ts
882
992
  function createCliBaseEvent(context = {}) {
@@ -944,7 +1054,7 @@ function formatJsonOutput(data) {
944
1054
  util.inspect(data, { colors: true, depth: null, breakLength: 80 })
945
1055
  );
946
1056
  }
947
- function formatItemsFromSchema(functionInfo, items, startingNumber = 0) {
1057
+ async function formatItemsFromSchema(functionInfo, items, startingNumber = 0, options) {
948
1058
  const outputSchema = functionInfo.outputSchema || getOutputSchema(functionInfo.inputSchema);
949
1059
  if (!outputSchema) {
950
1060
  formatItemsGeneric(items, startingNumber);
@@ -985,11 +1095,39 @@ function formatSingleItem(formatted, itemNumber) {
985
1095
  return;
986
1096
  }
987
1097
  for (const detail of formatted.details) {
988
- const styledText = applyStyle(detail.text, detail.style);
989
- console.log(` ${styledText}`);
1098
+ if (detail.label) {
1099
+ const isMultiline = detail.text.includes("\n");
1100
+ if (isMultiline) {
1101
+ console.log(` ${chalk7.gray(detail.label + ":")}`);
1102
+ const displayText = formatDetailText(
1103
+ detail.text,
1104
+ DETAIL_INDENT + " "
1105
+ );
1106
+ const styledText = applyStyle(displayText, detail.style);
1107
+ console.log(`${DETAIL_INDENT} ${styledText}`);
1108
+ } else {
1109
+ const styledValue = applyStyle(detail.text, detail.style);
1110
+ console.log(` ${chalk7.gray(detail.label + ":")} ${styledValue}`);
1111
+ }
1112
+ } else {
1113
+ const displayText = formatDetailText(detail.text, DETAIL_INDENT);
1114
+ const styledText = applyStyle(displayText, detail.style);
1115
+ console.log(` ${styledText}`);
1116
+ }
990
1117
  }
991
1118
  console.log();
992
1119
  }
1120
+ var DETAIL_INDENT = " ";
1121
+ var DETAIL_MAX_LINES = 5;
1122
+ function formatDetailText(text, indent = DETAIL_INDENT) {
1123
+ const columns = Math.max((process.stdout.columns || 80) - indent.length, 40);
1124
+ const wrapped = wrapAnsi(text, columns, { hard: true, trim: false });
1125
+ const lines = wrapped.split("\n");
1126
+ if (lines.length <= DETAIL_MAX_LINES) {
1127
+ return lines.join("\n" + indent);
1128
+ }
1129
+ return lines.slice(0, DETAIL_MAX_LINES).join("\n" + indent) + "\n" + indent + "\u2026";
1130
+ }
993
1131
  function applyStyle(value, style) {
994
1132
  switch (style) {
995
1133
  case "dim":
@@ -1336,15 +1474,16 @@ var CONFIRM_MESSAGES = {
1336
1474
  messageBefore: "You are about to create a sensitive secret that will be displayed as plain text.\nOnce created, you cannot retrieve it again.",
1337
1475
  messageAfter: "Please treat this secret like a password and store it securely!"
1338
1476
  },
1339
- delete: {
1340
- messageBefore: "You are about to delete this record."
1341
- }
1477
+ delete: (itemType) => ({
1478
+ messageBefore: `You are about to delete the ${itemType || "item"}.`
1479
+ })
1342
1480
  };
1343
- async function promptConfirm(confirmType) {
1481
+ async function promptConfirm(confirmType, itemType) {
1344
1482
  if (!confirmType || !CONFIRM_MESSAGES[confirmType]) {
1345
1483
  return { confirmed: true };
1346
1484
  }
1347
- const { messageBefore, messageAfter } = CONFIRM_MESSAGES[confirmType];
1485
+ const configOrFn = CONFIRM_MESSAGES[confirmType];
1486
+ const { messageBefore, messageAfter } = typeof configOrFn === "function" ? configOrFn(itemType) : configOrFn;
1348
1487
  console.log(chalk7.yellow(`
1349
1488
  ${messageBefore}
1350
1489
  `));
@@ -1428,6 +1567,7 @@ function analyzeZodField(name, schema, functionInfo) {
1428
1567
  }
1429
1568
  }
1430
1569
  let paramType = "string";
1570
+ let elementType;
1431
1571
  let choices;
1432
1572
  if (baseSchema instanceof z.ZodString) {
1433
1573
  paramType = "string";
@@ -1437,6 +1577,14 @@ function analyzeZodField(name, schema, functionInfo) {
1437
1577
  paramType = "boolean";
1438
1578
  } else if (baseSchema instanceof z.ZodArray) {
1439
1579
  paramType = "array";
1580
+ const elementSchema = baseSchema._zod.def.element;
1581
+ if (elementSchema instanceof z.ZodObject || elementSchema instanceof z.ZodRecord) {
1582
+ elementType = "object";
1583
+ } else if (elementSchema instanceof z.ZodNumber) {
1584
+ elementType = "number";
1585
+ } else if (elementSchema instanceof z.ZodBoolean) {
1586
+ elementType = "boolean";
1587
+ }
1440
1588
  } else if (baseSchema instanceof z.ZodEnum) {
1441
1589
  paramType = "string";
1442
1590
  choices = baseSchema.options;
@@ -1455,7 +1603,8 @@ function analyzeZodField(name, schema, functionInfo) {
1455
1603
  default: defaultValue,
1456
1604
  choices,
1457
1605
  hasResolver: paramHasResolver,
1458
- isPositional: isPositional(schema)
1606
+ isPositional: isPositional(schema),
1607
+ elementType
1459
1608
  };
1460
1609
  }
1461
1610
  function analyzeInputParameters(inputParameters, functionInfo) {
@@ -1642,7 +1791,10 @@ function createCommandConfig(cliCommandName, functionInfo, sdk2) {
1642
1791
  rawParams,
1643
1792
  sdk2,
1644
1793
  functionInfo.name,
1645
- { interactiveMode }
1794
+ {
1795
+ interactiveMode,
1796
+ debug: !!options2.debug || process.env.DEBUG === "true" || process.argv.includes("--debug")
1797
+ }
1646
1798
  );
1647
1799
  } else {
1648
1800
  resolvedParams = rawParams;
@@ -1650,7 +1802,10 @@ function createCommandConfig(cliCommandName, functionInfo, sdk2) {
1650
1802
  const confirm = functionInfo.confirm;
1651
1803
  let confirmMessageAfter;
1652
1804
  if (confirm && interactiveMode) {
1653
- const confirmResult = await promptConfirm(confirm);
1805
+ const confirmResult = await promptConfirm(
1806
+ confirm,
1807
+ functionInfo.itemType
1808
+ );
1654
1809
  if (!confirmResult.confirmed) {
1655
1810
  console.log(chalk7.yellow("Operation cancelled."));
1656
1811
  return;
@@ -1839,7 +1994,8 @@ function convertCliArgsToSdkParams(parameters, positionalArgs, options) {
1839
1994
  if ((param.required || param.isPositional) && argIndex < positionalArgs.length) {
1840
1995
  sdkParams[param.name] = convertValue(
1841
1996
  positionalArgs[argIndex],
1842
- param.type
1997
+ param.type,
1998
+ param.elementType
1843
1999
  );
1844
2000
  argIndex++;
1845
2001
  }
@@ -1851,12 +2007,12 @@ function convertCliArgsToSdkParams(parameters, positionalArgs, options) {
1851
2007
  if (param.type === "array" && Array.isArray(value) && value.length === 0) {
1852
2008
  return;
1853
2009
  }
1854
- sdkParams[camelKey] = convertValue(value, param.type);
2010
+ sdkParams[camelKey] = convertValue(value, param.type, param.elementType);
1855
2011
  }
1856
2012
  });
1857
2013
  return sdkParams;
1858
2014
  }
1859
- function convertValue(value, type) {
2015
+ function convertValue(value, type, elementType) {
1860
2016
  if (value === void 0) {
1861
2017
  return void 0;
1862
2018
  }
@@ -1865,8 +2021,20 @@ function convertValue(value, type) {
1865
2021
  return Number(value);
1866
2022
  case "boolean":
1867
2023
  return Boolean(value);
1868
- case "array":
1869
- return Array.isArray(value) ? value : [value];
2024
+ case "array": {
2025
+ const arr = Array.isArray(value) ? value : [value];
2026
+ if (elementType !== "object") return arr;
2027
+ return arr.flatMap((item) => {
2028
+ if (typeof item === "string" && (item.startsWith("{") || item.startsWith("["))) {
2029
+ try {
2030
+ return JSON.parse(item);
2031
+ } catch {
2032
+ return item;
2033
+ }
2034
+ }
2035
+ return item;
2036
+ });
2037
+ }
1870
2038
  case "string":
1871
2039
  return value;
1872
2040
  case "object":
@@ -4352,7 +4520,7 @@ function createZapierCliSdk(options = {}) {
4352
4520
  // package.json with { type: 'json' }
4353
4521
  var package_default2 = {
4354
4522
  name: "@zapier/zapier-sdk-cli",
4355
- version: "0.35.1"};
4523
+ version: "0.36.1"};
4356
4524
  var ONE_DAY_MS = 24 * 60 * 60 * 1e3;
4357
4525
  var CACHE_RESET_INTERVAL_MS = (() => {
4358
4526
  const { ZAPIER_SDK_UPDATE_CHECK_INTERVAL_MS = `${ONE_DAY_MS}` } = process.env;