@zapier/zapier-sdk-cli 0.36.3 → 0.37.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/README.md +48 -41
- package/dist/cli.cjs +128 -27
- package/dist/cli.mjs +129 -28
- package/dist/index.cjs +1 -1
- package/dist/index.mjs +1 -1
- package/dist/package.json +1 -1
- package/dist/src/cli.js +28 -2
- package/dist/src/utils/parameter-resolver.js +111 -21
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +4 -4
package/dist/src/cli.js
CHANGED
|
@@ -5,6 +5,8 @@ import { createZapierCliSdk } from "./sdk";
|
|
|
5
5
|
import packageJson from "../package.json" with { type: "json" };
|
|
6
6
|
import { ZapierCliError } from "./utils/errors";
|
|
7
7
|
import { checkAndNotifyUpdates } from "./utils/version-checker";
|
|
8
|
+
import { BaseSdkOptionsSchema } from "@zapier/zapier-sdk";
|
|
9
|
+
import { z } from "zod";
|
|
8
10
|
import { ReservedCliParameter, getReservedCliOption, } from "./utils/cli-options";
|
|
9
11
|
const EXIT_GRACE_PERIOD_MS = 500;
|
|
10
12
|
const program = new Command();
|
|
@@ -22,8 +24,24 @@ program
|
|
|
22
24
|
.option("--credentials-base-url <url>", "Base URL for authentication endpoints")
|
|
23
25
|
.option("--tracking-base-url <url>", "Base URL for Zapier tracking endpoints")
|
|
24
26
|
.option("--max-network-retries <count>", "Max retries for rate-limited requests (default: 3)")
|
|
25
|
-
.option("--max-network-retry-delay-ms <ms>", "Max delay in ms to wait for rate limit retry (default: 60000)")
|
|
26
|
-
|
|
27
|
+
.option("--max-network-retry-delay-ms <ms>", "Max delay in ms to wait for rate limit retry (default: 60000)");
|
|
28
|
+
// Dynamically register boolean flags from BaseSdkOptionsSchema
|
|
29
|
+
const booleanFlags = [];
|
|
30
|
+
for (const [key, fieldSchema] of Object.entries(
|
|
31
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
32
|
+
BaseSdkOptionsSchema.shape)) {
|
|
33
|
+
let inner = fieldSchema;
|
|
34
|
+
if (inner instanceof z.ZodOptional) {
|
|
35
|
+
inner = inner._zod.def.innerType;
|
|
36
|
+
}
|
|
37
|
+
if (inner instanceof z.ZodBoolean && key !== "debug") {
|
|
38
|
+
const kebab = key.replace(/([A-Z])/g, "-$1").toLowerCase();
|
|
39
|
+
const description = fieldSchema._zod?.def?.description ?? "";
|
|
40
|
+
booleanFlags.push({ camelName: key, kebabFlag: `--${kebab}` });
|
|
41
|
+
program.option(`--${kebab}`, description);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
program.helpOption(`${helpOption.short}, ${helpOption.flag}`, helpOption.description);
|
|
27
45
|
// Check for debug flag early (support both flag and env var)
|
|
28
46
|
const isDebugMode = process.env.DEBUG === "true" || process.argv.includes("--debug");
|
|
29
47
|
// Helper to get flag value from argv
|
|
@@ -77,6 +95,13 @@ function buildCredentialsFromFlags() {
|
|
|
77
95
|
return undefined;
|
|
78
96
|
}
|
|
79
97
|
const credentials = buildCredentialsFromFlags();
|
|
98
|
+
// Extract boolean flags from argv
|
|
99
|
+
const flagOverrides = {};
|
|
100
|
+
for (const { camelName, kebabFlag } of booleanFlags) {
|
|
101
|
+
if (process.argv.includes(kebabFlag)) {
|
|
102
|
+
flagOverrides[camelName] = true;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
80
105
|
// Create CLI SDK instance with all plugins and URL options
|
|
81
106
|
const sdk = createZapierCliSdk({
|
|
82
107
|
debug: isDebugMode,
|
|
@@ -85,6 +110,7 @@ const sdk = createZapierCliSdk({
|
|
|
85
110
|
trackingBaseUrl,
|
|
86
111
|
maxNetworkRetries,
|
|
87
112
|
maxNetworkRetryDelayMs,
|
|
113
|
+
...flagOverrides,
|
|
88
114
|
});
|
|
89
115
|
// Auth commands now handled by plugins
|
|
90
116
|
// Generate CLI commands from SDK schemas (including CLI plugins)
|
|
@@ -3,7 +3,7 @@ import chalk from "chalk";
|
|
|
3
3
|
import ora from "ora";
|
|
4
4
|
import { z } from "zod";
|
|
5
5
|
import { runWithTelemetryContext, } from "@zapier/zapier-sdk";
|
|
6
|
-
import { isPositional } from "@zapier/zapier-sdk";
|
|
6
|
+
import { isPositional, buildCapabilityMessage } from "@zapier/zapier-sdk";
|
|
7
7
|
import { ZapierCliUserCancellationError, ZapierCliMissingParametersError, ZapierCliValidationError, } from "./errors";
|
|
8
8
|
// Zod validation errors are structured objects; flatten them into a
|
|
9
9
|
// readable "field: reason" string for CLI error messages.
|
|
@@ -146,9 +146,11 @@ export class SchemaParameterResolver {
|
|
|
146
146
|
else {
|
|
147
147
|
for (const param of orderedRequiredParams) {
|
|
148
148
|
try {
|
|
149
|
-
const value = await this.resolveParameter(param, context, functionName);
|
|
150
|
-
|
|
151
|
-
|
|
149
|
+
const value = await this.resolveParameter(param, context, functionName, { isOptional: !param.isRequired });
|
|
150
|
+
if (param.isRequired || value !== undefined) {
|
|
151
|
+
this.setNestedValue(resolvedParams, param.path, value);
|
|
152
|
+
context.resolvedParams = resolvedParams;
|
|
153
|
+
}
|
|
152
154
|
}
|
|
153
155
|
catch (error) {
|
|
154
156
|
if (this.isUserCancellation(error)) {
|
|
@@ -325,28 +327,116 @@ export class SchemaParameterResolver {
|
|
|
325
327
|
}
|
|
326
328
|
this.debugLog(`Fetching options for ${promptLabel}`);
|
|
327
329
|
const fetchResult = await dynamicResolver.fetch(context.sdk, context.resolvedParams);
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
330
|
+
// Check if the result is an AsyncIterable of pages (from toIterable())
|
|
331
|
+
let pageIterator = null;
|
|
332
|
+
let items;
|
|
333
|
+
let hasMore = false;
|
|
334
|
+
if (fetchResult != null &&
|
|
335
|
+
typeof fetchResult === "object" &&
|
|
336
|
+
Symbol.asyncIterator in fetchResult) {
|
|
337
|
+
// Paginated: pull the first page from the iterator
|
|
338
|
+
pageIterator = fetchResult[Symbol.asyncIterator]();
|
|
339
|
+
const first = await pageIterator.next();
|
|
340
|
+
if (!first.done && first.value) {
|
|
341
|
+
items = first.value.data;
|
|
342
|
+
hasMore = first.value.nextCursor != null;
|
|
343
|
+
}
|
|
344
|
+
else {
|
|
345
|
+
items = [];
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
else if (fetchResult != null &&
|
|
349
|
+
typeof fetchResult === "object" &&
|
|
350
|
+
"data" in fetchResult) {
|
|
351
|
+
const page = fetchResult;
|
|
352
|
+
items = page.data;
|
|
353
|
+
hasMore = page.nextCursor != null;
|
|
354
|
+
if (hasMore) {
|
|
355
|
+
this.debugLog(`Resolver for ${promptLabel} has more pages but no iterator. ` +
|
|
356
|
+
`Use toIterable() to enable "Load more..." support.`);
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
else {
|
|
360
|
+
items = fetchResult || [];
|
|
361
|
+
pageIterator = null;
|
|
362
|
+
}
|
|
363
|
+
const LOAD_MORE_SENTINEL = Symbol("LOAD_MORE");
|
|
364
|
+
const SKIP_SENTINEL = Symbol("SKIP");
|
|
365
|
+
let newItemsStartIndex = -1;
|
|
334
366
|
this.stopSpinner();
|
|
335
|
-
|
|
336
|
-
const
|
|
337
|
-
promptConfig.
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
367
|
+
while (true) {
|
|
368
|
+
const promptConfig = dynamicResolver.prompt(items, context.resolvedParams);
|
|
369
|
+
promptConfig.name = promptName;
|
|
370
|
+
if (isOptional && promptConfig.choices) {
|
|
371
|
+
promptConfig.choices.unshift({
|
|
372
|
+
name: chalk.dim("(Skip)"),
|
|
373
|
+
value: SKIP_SENTINEL,
|
|
374
|
+
});
|
|
375
|
+
}
|
|
376
|
+
// Add "Load more..." option if paginated
|
|
377
|
+
if (hasMore && pageIterator && promptConfig.choices) {
|
|
378
|
+
promptConfig.choices.push({
|
|
379
|
+
name: chalk.dim("(Load more...)"),
|
|
380
|
+
value: LOAD_MORE_SENTINEL,
|
|
381
|
+
});
|
|
382
|
+
}
|
|
383
|
+
// Show hints for capabilities that would expand results
|
|
384
|
+
if (!hasMore &&
|
|
385
|
+
promptConfig.choices &&
|
|
386
|
+
dynamicResolver.requireCapabilities) {
|
|
387
|
+
const capContext = context.sdk.getContext();
|
|
388
|
+
if (capContext.hasCapability) {
|
|
389
|
+
for (const cap of dynamicResolver.requireCapabilities) {
|
|
390
|
+
const enabled = await capContext.hasCapability(cap);
|
|
391
|
+
if (!enabled) {
|
|
392
|
+
promptConfig.choices.push({
|
|
393
|
+
name: chalk.dim(buildCapabilityMessage(cap)),
|
|
394
|
+
value: SKIP_SENTINEL,
|
|
395
|
+
disabled: true,
|
|
396
|
+
});
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
// After loading more, jump cursor to first new item.
|
|
402
|
+
// Offset by the number of injected choices before the data items (e.g. Skip).
|
|
403
|
+
if (newItemsStartIndex >= 0 && promptConfig.choices) {
|
|
404
|
+
const injectedBefore = isOptional ? 1 : 0;
|
|
405
|
+
const adjustedIndex = newItemsStartIndex + injectedBefore;
|
|
406
|
+
if (promptConfig.choices[adjustedIndex]) {
|
|
407
|
+
promptConfig.default = promptConfig.choices[adjustedIndex].value;
|
|
408
|
+
}
|
|
409
|
+
newItemsStartIndex = -1;
|
|
410
|
+
}
|
|
341
411
|
const answers = await inquirer.prompt([promptConfig]);
|
|
342
|
-
|
|
343
|
-
if (
|
|
412
|
+
let selected = answers[promptName];
|
|
413
|
+
if (selected === SKIP_SENTINEL) {
|
|
344
414
|
return undefined;
|
|
345
415
|
}
|
|
346
|
-
|
|
416
|
+
const wantsMore = Array.isArray(selected)
|
|
417
|
+
? selected.includes(LOAD_MORE_SENTINEL)
|
|
418
|
+
: selected === LOAD_MORE_SENTINEL;
|
|
419
|
+
if (wantsMore && pageIterator) {
|
|
420
|
+
if (Array.isArray(selected)) {
|
|
421
|
+
selected = selected.filter((v) => v !== LOAD_MORE_SENTINEL);
|
|
422
|
+
}
|
|
423
|
+
const prevLength = items.length;
|
|
424
|
+
this.startSpinner();
|
|
425
|
+
this.debugLog("Fetching more options...");
|
|
426
|
+
const next = await pageIterator.next();
|
|
427
|
+
this.stopSpinner();
|
|
428
|
+
if (!next.done && next.value) {
|
|
429
|
+
items = [...items, ...next.value.data];
|
|
430
|
+
hasMore = next.value.nextCursor != null;
|
|
431
|
+
newItemsStartIndex = prevLength;
|
|
432
|
+
}
|
|
433
|
+
else {
|
|
434
|
+
hasMore = false;
|
|
435
|
+
}
|
|
436
|
+
continue;
|
|
437
|
+
}
|
|
438
|
+
return selected;
|
|
347
439
|
}
|
|
348
|
-
const answers = await inquirer.prompt([promptConfig]);
|
|
349
|
-
return answers[promptName];
|
|
350
440
|
}
|
|
351
441
|
else if (resolver.type === "fields") {
|
|
352
442
|
if (isOptional && !inArrayContext) {
|