@zapier/zapier-sdk-cli 0.61.3 → 0.63.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.
@@ -991,6 +991,39 @@ async function resolveCredentialsBaseUrl(context) {
991
991
  return getBaseUrlFromResolvedCredentials(resolvedCredentials) ?? getBaseUrlFromOptionsCredentials(context.options?.credentials) ?? context.options?.baseUrl;
992
992
  }
993
993
 
994
+ // src/utils/cli-argument-sanitizer.ts
995
+ var SENSITIVE_FLAGS = [
996
+ "--credentials",
997
+ "--credentials-client-secret",
998
+ "--credentials-client-id",
999
+ "--credentials-base-url",
1000
+ "--callback-url",
1001
+ "--user",
1002
+ "--header",
1003
+ "-H",
1004
+ "-u"
1005
+ ];
1006
+ function sanitizeCliArguments(args) {
1007
+ const sanitized = [];
1008
+ let skipNext = false;
1009
+ for (const arg of args) {
1010
+ if (skipNext) {
1011
+ skipNext = false;
1012
+ sanitized.push("[REDACTED]");
1013
+ continue;
1014
+ }
1015
+ if (SENSITIVE_FLAGS.some((flag) => arg.startsWith(flag + "="))) {
1016
+ sanitized.push(arg.split("=")[0] + "=[REDACTED]");
1017
+ } else if (SENSITIVE_FLAGS.includes(arg)) {
1018
+ sanitized.push(arg);
1019
+ skipNext = true;
1020
+ } else {
1021
+ sanitized.push(arg);
1022
+ }
1023
+ }
1024
+ return sanitized;
1025
+ }
1026
+
994
1027
  // src/utils/non-interactive.ts
995
1028
  function resolveNonInteractive(options) {
996
1029
  return options.nonInteractive === true || options.skipPrompts === true || !process.stdin.isTTY || !process.stdout.isTTY;
@@ -1310,6 +1343,14 @@ var client_default = api;
1310
1343
 
1311
1344
  // src/utils/auth/oauth-transaction.ts
1312
1345
  var OAUTH_LOOPBACK_HOST = "localhost";
1346
+ var OauthTransactionSchema = zod.z.object({
1347
+ browserAuthUrl: zod.z.string(),
1348
+ clientId: zod.z.string(),
1349
+ codeVerifier: zod.z.string(),
1350
+ redirectUri: zod.z.string(),
1351
+ state: zod.z.string(),
1352
+ tokenUrl: zod.z.string()
1353
+ });
1313
1354
  function buildBrowserAuthUrl({
1314
1355
  authorizeUrl,
1315
1356
  entryPoint = "login"
@@ -1398,8 +1439,51 @@ async function exchangeOauthCode({
1398
1439
  expiresIn: data.expires_in
1399
1440
  };
1400
1441
  }
1442
+ var PENDING_OAUTH_TRANSACTION_KEY = "pending_oauth_transaction";
1443
+ var PendingOauthTransactionSchema = zod.z.object({
1444
+ version: zod.z.literal(1),
1445
+ entryPoint: zod.z.enum(["login", "signup"]),
1446
+ expiresAt: zod.z.number(),
1447
+ credentialsBaseUrl: zod.z.string().optional(),
1448
+ credentialName: zod.z.string().optional(),
1449
+ useApprovals: zod.z.boolean(),
1450
+ headless: zod.z.boolean().default(false),
1451
+ transaction: OauthTransactionSchema
1452
+ });
1453
+ function savePendingOauthTransaction({
1454
+ entryPoint,
1455
+ transaction,
1456
+ expiresAt,
1457
+ credentialsBaseUrl,
1458
+ credentialName,
1459
+ useApprovals,
1460
+ headless
1461
+ }) {
1462
+ const pendingTransaction = {
1463
+ version: 1,
1464
+ entryPoint,
1465
+ expiresAt,
1466
+ useApprovals,
1467
+ headless,
1468
+ transaction,
1469
+ ...credentialsBaseUrl !== void 0 && { credentialsBaseUrl },
1470
+ ...credentialName !== void 0 && { credentialName }
1471
+ };
1472
+ getConfig().set(PENDING_OAUTH_TRANSACTION_KEY, pendingTransaction);
1473
+ return pendingTransaction;
1474
+ }
1475
+ function getPendingOauthTransaction() {
1476
+ const result = PendingOauthTransactionSchema.safeParse(
1477
+ getConfig().get(PENDING_OAUTH_TRANSACTION_KEY)
1478
+ );
1479
+ return result.success ? result.data : void 0;
1480
+ }
1481
+ function clearPendingOauthTransaction() {
1482
+ getConfig().delete(PENDING_OAUTH_TRANSACTION_KEY);
1483
+ }
1401
1484
 
1402
1485
  // src/utils/auth/oauth-flow.ts
1486
+ var PENDING_OAUTH_RECOVERY_MESSAGE = "Start a new non-interactive login or signup flow to generate a fresh URL.";
1403
1487
  var LOGIN_OAUTH_COPY = {
1404
1488
  flowName: "Login",
1405
1489
  action: "log in",
@@ -1455,6 +1539,95 @@ async function runSignupOauthFlow(options) {
1455
1539
  copy: SIGNUP_OAUTH_COPY
1456
1540
  });
1457
1541
  }
1542
+ async function startPendingOauthFlow({
1543
+ timeoutMs = LOGIN_TIMEOUT_MS,
1544
+ pkceCredentials,
1545
+ baseUrl,
1546
+ entryPoint,
1547
+ credentialName,
1548
+ useApprovals,
1549
+ headless
1550
+ }) {
1551
+ const copy = entryPoint === "signup" ? SIGNUP_OAUTH_COPY : LOGIN_OAUTH_COPY;
1552
+ const port = LOGIN_PORTS[0];
1553
+ const transaction = await prepareOauthTransaction({
1554
+ pkceCredentials,
1555
+ baseUrl,
1556
+ redirectUri: `http://${OAUTH_LOOPBACK_HOST}:${port}/oauth`,
1557
+ entryPoint
1558
+ });
1559
+ const pending = savePendingOauthTransaction({
1560
+ entryPoint,
1561
+ transaction,
1562
+ expiresAt: Date.now() + timeoutMs,
1563
+ credentialsBaseUrl: baseUrl,
1564
+ credentialName,
1565
+ useApprovals,
1566
+ headless
1567
+ });
1568
+ process.stderr.write(
1569
+ `Open this ${copy.urlLabel} in a browser to continue:
1570
+ `
1571
+ );
1572
+ console.log(transaction.browserAuthUrl);
1573
+ if (!headless) {
1574
+ process.stderr.write(`Opening your browser to ${entryPoint}.
1575
+ `);
1576
+ try {
1577
+ await open__default.default(transaction.browserAuthUrl);
1578
+ } catch (error) {
1579
+ const reason = error instanceof Error ? error.message : String(error);
1580
+ process.stderr.write(
1581
+ `Browser did not open automatically to ${entryPoint}: ${reason}
1582
+ `
1583
+ );
1584
+ }
1585
+ }
1586
+ process.stderr.write(
1587
+ `After authorizing, finish with \`zapier-sdk ${entryPoint} --callback-url <final-url>\`.
1588
+ `
1589
+ );
1590
+ return pending;
1591
+ }
1592
+ function getPendingOauthFlow({
1593
+ entryPoint
1594
+ }) {
1595
+ const pending = getPendingOauthTransaction();
1596
+ if (!pending) {
1597
+ throw new ZapierCliValidationError(
1598
+ `No pending ${entryPoint} OAuth flow found. ${PENDING_OAUTH_RECOVERY_MESSAGE}`
1599
+ );
1600
+ }
1601
+ if (Date.now() > pending.expiresAt) {
1602
+ clearPendingOauthTransaction();
1603
+ throw new ZapierCliValidationError(
1604
+ `Pending ${pending.entryPoint} OAuth flow has expired. ${PENDING_OAUTH_RECOVERY_MESSAGE}`
1605
+ );
1606
+ }
1607
+ if (pending.entryPoint !== entryPoint) {
1608
+ throw new ZapierCliValidationError(
1609
+ `Pending OAuth flow is for ${pending.entryPoint}, not ${entryPoint}. ${PENDING_OAUTH_RECOVERY_MESSAGE}`
1610
+ );
1611
+ }
1612
+ return pending;
1613
+ }
1614
+ async function resumePendingOauthFlow({
1615
+ pending,
1616
+ callbackUrl,
1617
+ onProgress
1618
+ }) {
1619
+ const code = getCallbackCode({
1620
+ callbackUrl,
1621
+ transaction: pending.transaction
1622
+ });
1623
+ onProgress?.({ type: "callback_accepted" });
1624
+ process.stderr.write("Exchanging authorization code for tokens...\n");
1625
+ onProgress?.({ type: "token_exchange_started" });
1626
+ const tokens = await exchangeOauthCode({ ...pending.transaction, code });
1627
+ clearPendingOauthTransaction();
1628
+ onProgress?.({ type: "token_exchange_completed" });
1629
+ return tokens;
1630
+ }
1458
1631
  function runOauthFlowForEntryPoint({
1459
1632
  options,
1460
1633
  entryPoint,
@@ -1990,12 +2163,19 @@ async function saveClientCredentials({
1990
2163
  function emitAccountAuthSuccess({
1991
2164
  sdk,
1992
2165
  profile,
1993
- clientId
2166
+ clientId,
2167
+ isNonInteractive,
2168
+ isHeadless
1994
2169
  }) {
1995
2170
  sdk.context.eventEmission.emit(
1996
2171
  "platform.sdk.ApplicationLifecycleEvent",
1997
2172
  zapierSdk.buildApplicationLifecycleEvent(
1998
- { lifecycle_event_type: "login_success" },
2173
+ {
2174
+ lifecycle_event_type: "login_success",
2175
+ used_non_interactive_mode: isNonInteractive,
2176
+ is_headless: isHeadless,
2177
+ process_argv: sanitizeCliArguments(process.argv.slice(2))
2178
+ },
1999
2179
  {
2000
2180
  customuser_id: profile.user_id,
2001
2181
  account_id: profile.roles[0]?.account_id ?? null,
@@ -2006,11 +2186,18 @@ function emitAccountAuthSuccess({
2006
2186
  );
2007
2187
  }
2008
2188
  function emitSignupSuccess({
2009
- sdk
2189
+ sdk,
2190
+ isNonInteractive,
2191
+ isHeadless
2010
2192
  }) {
2011
2193
  sdk.context.eventEmission.emit(
2012
2194
  "platform.sdk.ApplicationLifecycleEvent",
2013
- zapierSdk.buildApplicationLifecycleEvent({ lifecycle_event_type: "signup_success" })
2195
+ zapierSdk.buildApplicationLifecycleEvent({
2196
+ lifecycle_event_type: "signup_success",
2197
+ used_non_interactive_mode: isNonInteractive,
2198
+ is_headless: isHeadless,
2199
+ process_argv: sanitizeCliArguments(process.argv.slice(2))
2200
+ })
2014
2201
  );
2015
2202
  }
2016
2203
  async function runOauthWithRedaction(runOauth) {
@@ -2041,7 +2228,11 @@ async function runOauthForEntryPoint({
2041
2228
  recoveryMessage: headless ? HEADLESS_SIGNUP_RECOVERY_MESSAGE : SIGNUP_RECOVERY_MESSAGE,
2042
2229
  onProgress: (event) => {
2043
2230
  if (event.type === "callback_accepted") {
2044
- emitSignupSuccess({ sdk });
2231
+ emitSignupSuccess({
2232
+ sdk,
2233
+ isNonInteractive: false,
2234
+ isHeadless: headless === true
2235
+ });
2045
2236
  }
2046
2237
  }
2047
2238
  })
@@ -2058,11 +2249,124 @@ async function runOauthForEntryPoint({
2058
2249
  })
2059
2250
  );
2060
2251
  }
2252
+ async function provisionAccountCredentials({
2253
+ sdk,
2254
+ entryPoint,
2255
+ accessToken,
2256
+ credentialsBaseUrl,
2257
+ credentialName,
2258
+ interactive,
2259
+ useApprovals,
2260
+ isNonInteractive,
2261
+ isHeadless
2262
+ }) {
2263
+ const scopedApi = zapierSdk.getOrCreateApiClient({
2264
+ credentials: accessToken,
2265
+ baseUrl: credentialsBaseUrl,
2266
+ callerPackage: sdk.context.options?.callerPackage
2267
+ });
2268
+ const profile = await getProfile(scopedApi);
2269
+ process.stderr.write(`${getProfileMessage(entryPoint, profile.email)}
2270
+ `);
2271
+ process.stderr.write(
2272
+ "\nGenerating credentials so this machine can make authenticated requests on your behalf.\n"
2273
+ );
2274
+ const resolvedCredentialName = credentialName ?? await resolveCredentialName({
2275
+ email: profile.email,
2276
+ interactive,
2277
+ baseUrl: credentialsBaseUrl,
2278
+ entryPoint
2279
+ });
2280
+ const { clientId } = await saveClientCredentials({
2281
+ api: scopedApi,
2282
+ name: resolvedCredentialName,
2283
+ credentialsBaseUrl,
2284
+ useApprovals,
2285
+ cleanupLogPrefix: entryPoint
2286
+ });
2287
+ process.stderr.write(
2288
+ `\u2705 Credentials "${resolvedCredentialName}" created and set as default. You are ready to use the Zapier SDK.
2289
+ `
2290
+ );
2291
+ if (useApprovals) {
2292
+ process.stderr.write("\u{1F510} Approvals are enabled for these credentials.\n");
2293
+ }
2294
+ emitAccountAuthSuccess({
2295
+ sdk,
2296
+ profile,
2297
+ clientId,
2298
+ isNonInteractive,
2299
+ isHeadless
2300
+ });
2301
+ }
2061
2302
  async function runAccountAuth({
2062
2303
  sdk,
2063
2304
  options,
2064
2305
  entryPoint
2065
2306
  }) {
2307
+ if (options.callbackUrl !== void 0) {
2308
+ const { callbackUrl } = options;
2309
+ const pending = getPendingOauthFlow({ entryPoint });
2310
+ const finishName = options.name !== void 0 ? validateCredentialsName(options.name) : void 0;
2311
+ if (finishName !== void 0 && finishName !== pending.credentialName) {
2312
+ throw new ZapierCliValidationError(
2313
+ "Cannot change --name while completing a pending OAuth flow. Start a new non-interactive login with the desired --name."
2314
+ );
2315
+ }
2316
+ if (options.useApprovals === true && !pending.useApprovals) {
2317
+ throw new ZapierCliValidationError(
2318
+ "Cannot add --use-approvals while completing a pending OAuth flow. Start a new non-interactive flow with --use-approvals."
2319
+ );
2320
+ }
2321
+ if (pending.credentialName !== void 0) {
2322
+ const activeCredentials = getActiveCredentials({
2323
+ baseUrl: pending.credentialsBaseUrl
2324
+ });
2325
+ if (activeCredentials?.name !== pending.credentialName && credentialNameExists({
2326
+ name: pending.credentialName,
2327
+ baseUrl: pending.credentialsBaseUrl
2328
+ })) {
2329
+ throw new ZapierCliValidationError(
2330
+ `A credential named "${pending.credentialName}" already exists. Choose a different --name.`
2331
+ );
2332
+ }
2333
+ }
2334
+ if (!await clearExistingAuthState({
2335
+ sdk,
2336
+ baseUrl: pending.credentialsBaseUrl,
2337
+ interactive: false,
2338
+ entryPoint
2339
+ })) {
2340
+ return;
2341
+ }
2342
+ const { accessToken: accessToken2 } = await runOauthWithRedaction(async () => {
2343
+ return resumePendingOauthFlow({
2344
+ callbackUrl,
2345
+ pending,
2346
+ onProgress: (event) => {
2347
+ if (entryPoint === "signup" && event.type === "callback_accepted") {
2348
+ emitSignupSuccess({
2349
+ sdk,
2350
+ isNonInteractive: true,
2351
+ isHeadless: pending.headless
2352
+ });
2353
+ }
2354
+ }
2355
+ });
2356
+ });
2357
+ await provisionAccountCredentials({
2358
+ sdk,
2359
+ entryPoint,
2360
+ accessToken: accessToken2,
2361
+ credentialsBaseUrl: pending.credentialsBaseUrl,
2362
+ credentialName: pending.credentialName,
2363
+ interactive: false,
2364
+ useApprovals: pending.useApprovals,
2365
+ isNonInteractive: true,
2366
+ isHeadless: pending.headless
2367
+ });
2368
+ return;
2369
+ }
2066
2370
  const timeoutSeconds = parseTimeoutSeconds(options.timeout);
2067
2371
  const interactive = !resolveNonInteractive(options);
2068
2372
  const resolvedCredentials = await sdk.context.resolveCredentials();
@@ -2091,6 +2395,19 @@ async function runAccountAuth({
2091
2395
  })) {
2092
2396
  return;
2093
2397
  }
2398
+ const useApprovals = options.useApprovals === true;
2399
+ if (!interactive) {
2400
+ await startPendingOauthFlow({
2401
+ entryPoint,
2402
+ timeoutMs: timeoutSeconds * 1e3,
2403
+ pkceCredentials,
2404
+ baseUrl: credentialsBaseUrl,
2405
+ credentialName: providedName,
2406
+ useApprovals,
2407
+ headless
2408
+ });
2409
+ return;
2410
+ }
2094
2411
  const { accessToken } = await runOauthForEntryPoint({
2095
2412
  sdk,
2096
2413
  entryPoint,
@@ -2100,39 +2417,17 @@ async function runAccountAuth({
2100
2417
  headless,
2101
2418
  interactive
2102
2419
  });
2103
- const scopedApi = zapierSdk.getOrCreateApiClient({
2104
- credentials: accessToken,
2105
- baseUrl: credentialsBaseUrl,
2106
- callerPackage: sdk.context.options?.callerPackage
2107
- });
2108
- const profile = await getProfile(scopedApi);
2109
- process.stderr.write(`${getProfileMessage(entryPoint, profile.email)}
2110
- `);
2111
- process.stderr.write(
2112
- "\nGenerating credentials so this machine can make authenticated requests on your behalf.\n"
2113
- );
2114
- const credentialName = providedName ?? await resolveCredentialName({
2115
- email: profile.email,
2116
- interactive,
2117
- baseUrl: credentialsBaseUrl,
2118
- entryPoint
2119
- });
2120
- const useApprovals = options.useApprovals === true;
2121
- const { clientId } = await saveClientCredentials({
2122
- api: scopedApi,
2123
- name: credentialName,
2420
+ await provisionAccountCredentials({
2421
+ sdk,
2422
+ entryPoint,
2423
+ accessToken,
2124
2424
  credentialsBaseUrl,
2425
+ credentialName: providedName,
2426
+ interactive,
2125
2427
  useApprovals,
2126
- cleanupLogPrefix: entryPoint
2428
+ isNonInteractive: false,
2429
+ isHeadless: headless
2127
2430
  });
2128
- process.stderr.write(
2129
- `\u2705 Credentials "${credentialName}" created and set as default. You are ready to use the Zapier SDK.
2130
- `
2131
- );
2132
- if (useApprovals) {
2133
- process.stderr.write("\u{1F510} Approvals are enabled for these credentials.\n");
2134
- }
2135
- emitAccountAuthSuccess({ sdk, profile, clientId });
2136
2431
  }
2137
2432
  var LoginSchema = zod.z.object({
2138
2433
  name: zod.z.string().optional().describe(
@@ -2152,8 +2447,20 @@ var LoginSchema = zod.z.object({
2152
2447
  }),
2153
2448
  headless: zod.z.boolean().optional().describe(
2154
2449
  "Use when logging in from a machine that has no browser. Prints a login link to open elsewhere, then accepts the pasted loopback callback URL."
2450
+ ),
2451
+ callbackUrl: zod.z.string().optional().describe(
2452
+ "Resume a pending non-interactive login with the final OAuth callback URL from your browser."
2155
2453
  )
2156
- }).describe("Log in to Zapier to access your account");
2454
+ }).describe("Log in to Zapier to access your account").meta({
2455
+ cliTelemetry: {
2456
+ authFlow: {
2457
+ callbackUrlParam: "callbackUrl",
2458
+ headlessParam: "headless",
2459
+ nonInteractiveParam: "nonInteractive",
2460
+ skipPromptsParam: "skipPrompts"
2461
+ }
2462
+ }
2463
+ });
2157
2464
 
2158
2465
  // src/plugins/login/index.ts
2159
2466
  var loginPlugin = zapierSdk.definePlugin(
@@ -2182,8 +2489,20 @@ var SignupSchema = zod.z.object({
2182
2489
  }),
2183
2490
  headless: zod.z.boolean().optional().describe(
2184
2491
  "Use when signing up from a machine that has no browser. Prints a signup link to open elsewhere, then accepts the pasted loopback callback URL."
2492
+ ),
2493
+ callbackUrl: zod.z.string().optional().describe(
2494
+ "Resume a pending non-interactive signup with the final OAuth callback URL from your browser."
2185
2495
  )
2186
- }).describe("Set up Zapier account access and SDK credentials");
2496
+ }).describe("Set up Zapier account access and SDK credentials").meta({
2497
+ cliTelemetry: {
2498
+ authFlow: {
2499
+ callbackUrlParam: "callbackUrl",
2500
+ headlessParam: "headless",
2501
+ nonInteractiveParam: "nonInteractive",
2502
+ skipPromptsParam: "skipPrompts"
2503
+ }
2504
+ }
2505
+ });
2187
2506
 
2188
2507
  // src/plugins/signup/index.ts
2189
2508
  var signupPlugin = zapierSdk.definePlugin(
@@ -4741,7 +5060,7 @@ function collectDeprecationNoticeAndForward(event, onEvent) {
4741
5060
  // package.json with { type: 'json' }
4742
5061
  var package_default = {
4743
5062
  name: "@zapier/zapier-sdk-cli",
4744
- version: "0.61.3"};
5063
+ version: "0.63.0"};
4745
5064
 
4746
5065
  // src/experimental.ts
4747
5066
  experimental.injectCliLogin(login_exports);
@@ -1,5 +1,5 @@
1
1
  import { ZapierSdkOptions } from '@zapier/zapier-sdk/experimental';
2
- import { E as Extension, Z as ZapierSdkCli } from './extensions-DggdwXJF.mjs';
2
+ import { E as Extension, Z as ZapierSdkCli } from './extensions-N27h0wrp.mjs';
3
3
  import '@zapier/zapier-sdk';
4
4
  import 'zod';
5
5
 
@@ -1,5 +1,5 @@
1
1
  import { ZapierSdkOptions } from '@zapier/zapier-sdk/experimental';
2
- import { E as Extension, Z as ZapierSdkCli } from './extensions-DggdwXJF.js';
2
+ import { E as Extension, Z as ZapierSdkCli } from './extensions-N27h0wrp.js';
3
3
  import '@zapier/zapier-sdk';
4
4
  import 'zod';
5
5