@zapier/zapier-sdk-cli 0.54.1 → 0.54.2

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 CHANGED
@@ -1,5 +1,14 @@
1
1
  # @zapier/zapier-sdk-cli
2
2
 
3
+ ## 0.54.2
4
+
5
+ ### Patch Changes
6
+
7
+ - c50db26: Fixed logout so it reliably clears local authentication state even when the server cannot revoke the credential. Previously a failed revocation could abort logout and strand the user with credentials they could not remove. Re-login flows are unaffected; they still surface revocation failures before replacing credentials.
8
+ - Updated dependencies [c50db26]
9
+ - @zapier/zapier-sdk@0.69.2
10
+ - @zapier/zapier-sdk-mcp@0.13.26
11
+
3
12
  ## 0.54.1
4
13
 
5
14
  ### Patch Changes
package/dist/cli.cjs CHANGED
@@ -1573,7 +1573,7 @@ var SHARED_COMMAND_CLI_OPTIONS = [
1573
1573
 
1574
1574
  // package.json
1575
1575
  var package_default = {
1576
- version: "0.54.1"};
1576
+ version: "0.54.2"};
1577
1577
 
1578
1578
  // src/telemetry/builders.ts
1579
1579
  function createCliBaseEvent(context = {}) {
@@ -3470,7 +3470,8 @@ function sleep(ms) {
3470
3470
  async function withRetry({
3471
3471
  action,
3472
3472
  attempts = 3,
3473
- initialDelayMs = 100
3473
+ initialDelayMs = 100,
3474
+ shouldRetry = () => true
3474
3475
  }) {
3475
3476
  if (attempts <= 0) {
3476
3477
  throw new Error("withRetry: attempts must be greater than 0");
@@ -3481,9 +3482,8 @@ async function withRetry({
3481
3482
  return await action();
3482
3483
  } catch (err) {
3483
3484
  lastError = err;
3484
- if (i < attempts - 1) {
3485
- await sleep(initialDelayMs * 2 ** i);
3486
- }
3485
+ if (!shouldRetry(err) || i >= attempts - 1) break;
3486
+ await sleep(initialDelayMs * 2 ** i);
3487
3487
  }
3488
3488
  }
3489
3489
  throw lastError;
@@ -3503,32 +3503,50 @@ function getStatusCode(err) {
3503
3503
  }
3504
3504
  return void 0;
3505
3505
  }
3506
+ var REVOKE_WARNING_TAIL = "Local state will be cleared, but the credential may still be active. Verify or revoke via `list-client-credentials` or `delete-client-credentials`.";
3507
+ var REVOKE_REJECTED_WARNING = "Could not revoke the credential on the server: the request was rejected (your current credentials may lack permission to revoke it). Local state has been cleared, but the credential may still be active on the server \u2014 run `login` to re-authenticate, then `delete-client-credentials` to remove it.";
3506
3508
  async function revokeCredentials({
3507
3509
  api: api2,
3508
3510
  credentials: credentials2,
3509
- onEvent
3511
+ onEvent,
3512
+ alwaysClearLocalState = false
3510
3513
  }) {
3511
- await withRetry({
3512
- action: async () => {
3513
- try {
3514
- await api2.delete(
3515
- `/api/v0/client-credentials/${credentials2.clientId}`,
3516
- void 0,
3517
- { authRequired: true, requiredScopes: ["credentials"] }
3518
- );
3519
- } catch (err) {
3520
- const status = getStatusCode(err);
3521
- if (status === 404) return;
3522
- if (status === 401) {
3523
- console.warn(
3524
- "Could not revoke credentials on the server (unauthorized). Local state will be cleared, but the credential may still be active. Verify or revoke via `list-client-credentials` or `delete-client-credentials`."
3514
+ try {
3515
+ await withRetry({
3516
+ // Permanent errors (403/400) won't change on retry — fail fast instead of
3517
+ // burning the backoff budget. The outer catch then rethrows (re-login) or
3518
+ // swallows and clears local state (logout) per `alwaysClearLocalState`.
3519
+ shouldRetry: (err) => !zapierSdk.isPermanentHttpError(err),
3520
+ action: async () => {
3521
+ try {
3522
+ await api2.delete(
3523
+ `/api/v0/client-credentials/${credentials2.clientId}`,
3524
+ void 0,
3525
+ { authRequired: true, requiredScopes: ["credentials"] }
3525
3526
  );
3526
- return;
3527
+ } catch (err) {
3528
+ const status = getStatusCode(err);
3529
+ if (status === 404) return;
3530
+ if (status === 401) {
3531
+ console.warn(
3532
+ "Could not revoke credentials on the server (unauthorized). " + REVOKE_WARNING_TAIL
3533
+ );
3534
+ return;
3535
+ }
3536
+ throw err;
3527
3537
  }
3528
- throw err;
3529
3538
  }
3539
+ });
3540
+ } catch (err) {
3541
+ if (!alwaysClearLocalState) throw err;
3542
+ if (zapierSdk.isPermanentHttpError(err)) {
3543
+ console.warn(REVOKE_REJECTED_WARNING);
3544
+ } else {
3545
+ console.warn(
3546
+ "Could not revoke credentials on the server. " + REVOKE_WARNING_TAIL
3547
+ );
3530
3548
  }
3531
- });
3549
+ }
3532
3550
  try {
3533
3551
  await deleteStoredClientCredentials({
3534
3552
  name: credentials2.name,
@@ -4628,7 +4646,8 @@ var logoutPlugin = zapierSdk.definePlugin(
4628
4646
  await revokeCredentials({
4629
4647
  api: sdk2.context.api,
4630
4648
  credentials: activeCredentials,
4631
- onEvent
4649
+ onEvent,
4650
+ alwaysClearLocalState: true
4632
4651
  });
4633
4652
  console.log("\u2705 Successfully logged out");
4634
4653
  }
@@ -7153,7 +7172,7 @@ var watchTriggerInboxCliPlugin = zapierSdk.definePlugin(
7153
7172
  // package.json with { type: 'json' }
7154
7173
  var package_default2 = {
7155
7174
  name: "@zapier/zapier-sdk-cli",
7156
- version: "0.54.1"};
7175
+ version: "0.54.2"};
7157
7176
 
7158
7177
  // src/sdk.ts
7159
7178
  zapierSdk.injectCliLogin(login_exports);
package/dist/cli.mjs CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import { Command, CommanderError, Option } from 'commander';
3
3
  import { z } from 'zod';
4
- import { definePlugin, createPluginMethod, OutputPropertySchema, ZapierBundleError, DEFAULT_CONFIG_PATH, ZapierValidationError, ZapierUnknownError, ZapierReleaseTriggerMessageSignal, injectCliLogin, BaseSdkOptionsSchema, getOrCreateApiClient, invalidateCachedToken, batch, toSnakeCase, ZapierAbortDrainSignal, createZapierSdkStack as createZapierSdkStack$1, addPlugin as addPlugin$1, ZapierError, isCredentialsObject, buildApplicationLifecycleEvent, ZapierAuthenticationError, isPositional, runWithTelemetryContext, buildCapabilityMessage, formatErrorMessage, getOsInfo, getPlatformVersions, getCiPlatform, isCi, getReleaseId, getCurrentTimestamp, generateEventId } from '@zapier/zapier-sdk';
4
+ import { definePlugin, createPluginMethod, OutputPropertySchema, ZapierBundleError, DEFAULT_CONFIG_PATH, ZapierValidationError, ZapierUnknownError, ZapierReleaseTriggerMessageSignal, injectCliLogin, BaseSdkOptionsSchema, getOrCreateApiClient, isPermanentHttpError, invalidateCachedToken, batch, toSnakeCase, ZapierAbortDrainSignal, createZapierSdkStack as createZapierSdkStack$1, addPlugin as addPlugin$1, ZapierError, isCredentialsObject, buildApplicationLifecycleEvent, ZapierAuthenticationError, isPositional, runWithTelemetryContext, buildCapabilityMessage, formatErrorMessage, getOsInfo, getPlatformVersions, getCiPlatform, isCi, getReleaseId, getCurrentTimestamp, generateEventId } from '@zapier/zapier-sdk';
5
5
  import inquirer from 'inquirer';
6
6
  import search from '@inquirer/search';
7
7
  import chalk from 'chalk';
@@ -1531,7 +1531,7 @@ var SHARED_COMMAND_CLI_OPTIONS = [
1531
1531
 
1532
1532
  // package.json
1533
1533
  var package_default = {
1534
- version: "0.54.1"};
1534
+ version: "0.54.2"};
1535
1535
 
1536
1536
  // src/telemetry/builders.ts
1537
1537
  function createCliBaseEvent(context = {}) {
@@ -3428,7 +3428,8 @@ function sleep(ms) {
3428
3428
  async function withRetry({
3429
3429
  action,
3430
3430
  attempts = 3,
3431
- initialDelayMs = 100
3431
+ initialDelayMs = 100,
3432
+ shouldRetry = () => true
3432
3433
  }) {
3433
3434
  if (attempts <= 0) {
3434
3435
  throw new Error("withRetry: attempts must be greater than 0");
@@ -3439,9 +3440,8 @@ async function withRetry({
3439
3440
  return await action();
3440
3441
  } catch (err) {
3441
3442
  lastError = err;
3442
- if (i < attempts - 1) {
3443
- await sleep(initialDelayMs * 2 ** i);
3444
- }
3443
+ if (!shouldRetry(err) || i >= attempts - 1) break;
3444
+ await sleep(initialDelayMs * 2 ** i);
3445
3445
  }
3446
3446
  }
3447
3447
  throw lastError;
@@ -3461,32 +3461,50 @@ function getStatusCode(err) {
3461
3461
  }
3462
3462
  return void 0;
3463
3463
  }
3464
+ var REVOKE_WARNING_TAIL = "Local state will be cleared, but the credential may still be active. Verify or revoke via `list-client-credentials` or `delete-client-credentials`.";
3465
+ var REVOKE_REJECTED_WARNING = "Could not revoke the credential on the server: the request was rejected (your current credentials may lack permission to revoke it). Local state has been cleared, but the credential may still be active on the server \u2014 run `login` to re-authenticate, then `delete-client-credentials` to remove it.";
3464
3466
  async function revokeCredentials({
3465
3467
  api: api2,
3466
3468
  credentials: credentials2,
3467
- onEvent
3469
+ onEvent,
3470
+ alwaysClearLocalState = false
3468
3471
  }) {
3469
- await withRetry({
3470
- action: async () => {
3471
- try {
3472
- await api2.delete(
3473
- `/api/v0/client-credentials/${credentials2.clientId}`,
3474
- void 0,
3475
- { authRequired: true, requiredScopes: ["credentials"] }
3476
- );
3477
- } catch (err) {
3478
- const status = getStatusCode(err);
3479
- if (status === 404) return;
3480
- if (status === 401) {
3481
- console.warn(
3482
- "Could not revoke credentials on the server (unauthorized). Local state will be cleared, but the credential may still be active. Verify or revoke via `list-client-credentials` or `delete-client-credentials`."
3472
+ try {
3473
+ await withRetry({
3474
+ // Permanent errors (403/400) won't change on retry — fail fast instead of
3475
+ // burning the backoff budget. The outer catch then rethrows (re-login) or
3476
+ // swallows and clears local state (logout) per `alwaysClearLocalState`.
3477
+ shouldRetry: (err) => !isPermanentHttpError(err),
3478
+ action: async () => {
3479
+ try {
3480
+ await api2.delete(
3481
+ `/api/v0/client-credentials/${credentials2.clientId}`,
3482
+ void 0,
3483
+ { authRequired: true, requiredScopes: ["credentials"] }
3483
3484
  );
3484
- return;
3485
+ } catch (err) {
3486
+ const status = getStatusCode(err);
3487
+ if (status === 404) return;
3488
+ if (status === 401) {
3489
+ console.warn(
3490
+ "Could not revoke credentials on the server (unauthorized). " + REVOKE_WARNING_TAIL
3491
+ );
3492
+ return;
3493
+ }
3494
+ throw err;
3485
3495
  }
3486
- throw err;
3487
3496
  }
3497
+ });
3498
+ } catch (err) {
3499
+ if (!alwaysClearLocalState) throw err;
3500
+ if (isPermanentHttpError(err)) {
3501
+ console.warn(REVOKE_REJECTED_WARNING);
3502
+ } else {
3503
+ console.warn(
3504
+ "Could not revoke credentials on the server. " + REVOKE_WARNING_TAIL
3505
+ );
3488
3506
  }
3489
- });
3507
+ }
3490
3508
  try {
3491
3509
  await deleteStoredClientCredentials({
3492
3510
  name: credentials2.name,
@@ -4586,7 +4604,8 @@ var logoutPlugin = definePlugin(
4586
4604
  await revokeCredentials({
4587
4605
  api: sdk2.context.api,
4588
4606
  credentials: activeCredentials,
4589
- onEvent
4607
+ onEvent,
4608
+ alwaysClearLocalState: true
4590
4609
  });
4591
4610
  console.log("\u2705 Successfully logged out");
4592
4611
  }
@@ -7111,7 +7130,7 @@ var watchTriggerInboxCliPlugin = definePlugin(
7111
7130
  // package.json with { type: 'json' }
7112
7131
  var package_default2 = {
7113
7132
  name: "@zapier/zapier-sdk-cli",
7114
- version: "0.54.1"};
7133
+ version: "0.54.2"};
7115
7134
 
7116
7135
  // src/sdk.ts
7117
7136
  injectCliLogin(login_exports);
@@ -814,7 +814,8 @@ function sleep(ms) {
814
814
  async function withRetry({
815
815
  action,
816
816
  attempts = 3,
817
- initialDelayMs = 100
817
+ initialDelayMs = 100,
818
+ shouldRetry = () => true
818
819
  }) {
819
820
  if (attempts <= 0) {
820
821
  throw new Error("withRetry: attempts must be greater than 0");
@@ -825,9 +826,8 @@ async function withRetry({
825
826
  return await action();
826
827
  } catch (err) {
827
828
  lastError = err;
828
- if (i < attempts - 1) {
829
- await sleep(initialDelayMs * 2 ** i);
830
- }
829
+ if (!shouldRetry(err) || i >= attempts - 1) break;
830
+ await sleep(initialDelayMs * 2 ** i);
831
831
  }
832
832
  }
833
833
  throw lastError;
@@ -847,32 +847,50 @@ function getStatusCode(err) {
847
847
  }
848
848
  return void 0;
849
849
  }
850
+ var REVOKE_WARNING_TAIL = "Local state will be cleared, but the credential may still be active. Verify or revoke via `list-client-credentials` or `delete-client-credentials`.";
851
+ var REVOKE_REJECTED_WARNING = "Could not revoke the credential on the server: the request was rejected (your current credentials may lack permission to revoke it). Local state has been cleared, but the credential may still be active on the server \u2014 run `login` to re-authenticate, then `delete-client-credentials` to remove it.";
850
852
  async function revokeCredentials({
851
853
  api: api2,
852
854
  credentials,
853
- onEvent
855
+ onEvent,
856
+ alwaysClearLocalState = false
854
857
  }) {
855
- await withRetry({
856
- action: async () => {
857
- try {
858
- await api2.delete(
859
- `/api/v0/client-credentials/${credentials.clientId}`,
860
- void 0,
861
- { authRequired: true, requiredScopes: ["credentials"] }
862
- );
863
- } catch (err) {
864
- const status = getStatusCode(err);
865
- if (status === 404) return;
866
- if (status === 401) {
867
- console.warn(
868
- "Could not revoke credentials on the server (unauthorized). Local state will be cleared, but the credential may still be active. Verify or revoke via `list-client-credentials` or `delete-client-credentials`."
858
+ try {
859
+ await withRetry({
860
+ // Permanent errors (403/400) won't change on retry — fail fast instead of
861
+ // burning the backoff budget. The outer catch then rethrows (re-login) or
862
+ // swallows and clears local state (logout) per `alwaysClearLocalState`.
863
+ shouldRetry: (err) => !zapierSdk.isPermanentHttpError(err),
864
+ action: async () => {
865
+ try {
866
+ await api2.delete(
867
+ `/api/v0/client-credentials/${credentials.clientId}`,
868
+ void 0,
869
+ { authRequired: true, requiredScopes: ["credentials"] }
869
870
  );
870
- return;
871
+ } catch (err) {
872
+ const status = getStatusCode(err);
873
+ if (status === 404) return;
874
+ if (status === 401) {
875
+ console.warn(
876
+ "Could not revoke credentials on the server (unauthorized). " + REVOKE_WARNING_TAIL
877
+ );
878
+ return;
879
+ }
880
+ throw err;
871
881
  }
872
- throw err;
873
882
  }
883
+ });
884
+ } catch (err) {
885
+ if (!alwaysClearLocalState) throw err;
886
+ if (zapierSdk.isPermanentHttpError(err)) {
887
+ console.warn(REVOKE_REJECTED_WARNING);
888
+ } else {
889
+ console.warn(
890
+ "Could not revoke credentials on the server. " + REVOKE_WARNING_TAIL
891
+ );
874
892
  }
875
- });
893
+ }
876
894
  try {
877
895
  await deleteStoredClientCredentials({
878
896
  name: credentials.name,
@@ -1998,7 +2016,8 @@ var logoutPlugin = zapierSdk.definePlugin(
1998
2016
  await revokeCredentials({
1999
2017
  api: sdk2.context.api,
2000
2018
  credentials: activeCredentials,
2001
- onEvent
2019
+ onEvent,
2020
+ alwaysClearLocalState: true
2002
2021
  });
2003
2022
  console.log("\u2705 Successfully logged out");
2004
2023
  }
@@ -4506,7 +4525,7 @@ var watchTriggerInboxCliPlugin = zapierSdk.definePlugin(
4506
4525
  // package.json with { type: 'json' }
4507
4526
  var package_default = {
4508
4527
  name: "@zapier/zapier-sdk-cli",
4509
- version: "0.54.1"};
4528
+ version: "0.54.2"};
4510
4529
 
4511
4530
  // src/experimental.ts
4512
4531
  experimental.injectCliLogin(login_exports);
@@ -7,7 +7,7 @@ import crypto, { createHash } from 'crypto';
7
7
  import * as path from 'path';
8
8
  import { resolve, join, dirname, basename, relative, extname } from 'path';
9
9
  import * as lockfile from 'proper-lockfile';
10
- import { definePlugin, createPluginMethod, OutputPropertySchema, ZapierBundleError, DEFAULT_CONFIG_PATH, ZapierValidationError, ZapierUnknownError, ZapierReleaseTriggerMessageSignal, getOrCreateApiClient, invalidateCachedToken, batch, toSnakeCase, ZapierAbortDrainSignal, isCredentialsObject, buildApplicationLifecycleEvent, ZapierAuthenticationError, ZapierError } from '@zapier/zapier-sdk';
10
+ import { definePlugin, createPluginMethod, OutputPropertySchema, ZapierBundleError, DEFAULT_CONFIG_PATH, ZapierValidationError, ZapierUnknownError, ZapierReleaseTriggerMessageSignal, getOrCreateApiClient, isPermanentHttpError, invalidateCachedToken, batch, toSnakeCase, ZapierAbortDrainSignal, isCredentialsObject, buildApplicationLifecycleEvent, ZapierAuthenticationError, ZapierError } from '@zapier/zapier-sdk';
11
11
  import { z } from 'zod';
12
12
  import { injectCliLogin, createZapierSdkStack, addPlugin } from '@zapier/zapier-sdk/experimental';
13
13
  import { hostname } from 'os';
@@ -778,7 +778,8 @@ function sleep(ms) {
778
778
  async function withRetry({
779
779
  action,
780
780
  attempts = 3,
781
- initialDelayMs = 100
781
+ initialDelayMs = 100,
782
+ shouldRetry = () => true
782
783
  }) {
783
784
  if (attempts <= 0) {
784
785
  throw new Error("withRetry: attempts must be greater than 0");
@@ -789,9 +790,8 @@ async function withRetry({
789
790
  return await action();
790
791
  } catch (err) {
791
792
  lastError = err;
792
- if (i < attempts - 1) {
793
- await sleep(initialDelayMs * 2 ** i);
794
- }
793
+ if (!shouldRetry(err) || i >= attempts - 1) break;
794
+ await sleep(initialDelayMs * 2 ** i);
795
795
  }
796
796
  }
797
797
  throw lastError;
@@ -811,32 +811,50 @@ function getStatusCode(err) {
811
811
  }
812
812
  return void 0;
813
813
  }
814
+ var REVOKE_WARNING_TAIL = "Local state will be cleared, but the credential may still be active. Verify or revoke via `list-client-credentials` or `delete-client-credentials`.";
815
+ var REVOKE_REJECTED_WARNING = "Could not revoke the credential on the server: the request was rejected (your current credentials may lack permission to revoke it). Local state has been cleared, but the credential may still be active on the server \u2014 run `login` to re-authenticate, then `delete-client-credentials` to remove it.";
814
816
  async function revokeCredentials({
815
817
  api: api2,
816
818
  credentials,
817
- onEvent
819
+ onEvent,
820
+ alwaysClearLocalState = false
818
821
  }) {
819
- await withRetry({
820
- action: async () => {
821
- try {
822
- await api2.delete(
823
- `/api/v0/client-credentials/${credentials.clientId}`,
824
- void 0,
825
- { authRequired: true, requiredScopes: ["credentials"] }
826
- );
827
- } catch (err) {
828
- const status = getStatusCode(err);
829
- if (status === 404) return;
830
- if (status === 401) {
831
- console.warn(
832
- "Could not revoke credentials on the server (unauthorized). Local state will be cleared, but the credential may still be active. Verify or revoke via `list-client-credentials` or `delete-client-credentials`."
822
+ try {
823
+ await withRetry({
824
+ // Permanent errors (403/400) won't change on retry — fail fast instead of
825
+ // burning the backoff budget. The outer catch then rethrows (re-login) or
826
+ // swallows and clears local state (logout) per `alwaysClearLocalState`.
827
+ shouldRetry: (err) => !isPermanentHttpError(err),
828
+ action: async () => {
829
+ try {
830
+ await api2.delete(
831
+ `/api/v0/client-credentials/${credentials.clientId}`,
832
+ void 0,
833
+ { authRequired: true, requiredScopes: ["credentials"] }
833
834
  );
834
- return;
835
+ } catch (err) {
836
+ const status = getStatusCode(err);
837
+ if (status === 404) return;
838
+ if (status === 401) {
839
+ console.warn(
840
+ "Could not revoke credentials on the server (unauthorized). " + REVOKE_WARNING_TAIL
841
+ );
842
+ return;
843
+ }
844
+ throw err;
835
845
  }
836
- throw err;
837
846
  }
847
+ });
848
+ } catch (err) {
849
+ if (!alwaysClearLocalState) throw err;
850
+ if (isPermanentHttpError(err)) {
851
+ console.warn(REVOKE_REJECTED_WARNING);
852
+ } else {
853
+ console.warn(
854
+ "Could not revoke credentials on the server. " + REVOKE_WARNING_TAIL
855
+ );
838
856
  }
839
- });
857
+ }
840
858
  try {
841
859
  await deleteStoredClientCredentials({
842
860
  name: credentials.name,
@@ -1962,7 +1980,8 @@ var logoutPlugin = definePlugin(
1962
1980
  await revokeCredentials({
1963
1981
  api: sdk2.context.api,
1964
1982
  credentials: activeCredentials,
1965
- onEvent
1983
+ onEvent,
1984
+ alwaysClearLocalState: true
1966
1985
  });
1967
1986
  console.log("\u2705 Successfully logged out");
1968
1987
  }
@@ -4470,7 +4489,7 @@ var watchTriggerInboxCliPlugin = definePlugin(
4470
4489
  // package.json with { type: 'json' }
4471
4490
  var package_default = {
4472
4491
  name: "@zapier/zapier-sdk-cli",
4473
- version: "0.54.1"};
4492
+ version: "0.54.2"};
4474
4493
 
4475
4494
  // src/experimental.ts
4476
4495
  injectCliLogin(login_exports);
package/dist/index.cjs CHANGED
@@ -813,7 +813,8 @@ function sleep(ms) {
813
813
  async function withRetry({
814
814
  action,
815
815
  attempts = 3,
816
- initialDelayMs = 100
816
+ initialDelayMs = 100,
817
+ shouldRetry = () => true
817
818
  }) {
818
819
  if (attempts <= 0) {
819
820
  throw new Error("withRetry: attempts must be greater than 0");
@@ -824,9 +825,8 @@ async function withRetry({
824
825
  return await action();
825
826
  } catch (err) {
826
827
  lastError = err;
827
- if (i < attempts - 1) {
828
- await sleep(initialDelayMs * 2 ** i);
829
- }
828
+ if (!shouldRetry(err) || i >= attempts - 1) break;
829
+ await sleep(initialDelayMs * 2 ** i);
830
830
  }
831
831
  }
832
832
  throw lastError;
@@ -846,32 +846,50 @@ function getStatusCode(err) {
846
846
  }
847
847
  return void 0;
848
848
  }
849
+ var REVOKE_WARNING_TAIL = "Local state will be cleared, but the credential may still be active. Verify or revoke via `list-client-credentials` or `delete-client-credentials`.";
850
+ var REVOKE_REJECTED_WARNING = "Could not revoke the credential on the server: the request was rejected (your current credentials may lack permission to revoke it). Local state has been cleared, but the credential may still be active on the server \u2014 run `login` to re-authenticate, then `delete-client-credentials` to remove it.";
849
851
  async function revokeCredentials({
850
852
  api: api2,
851
853
  credentials,
852
- onEvent
854
+ onEvent,
855
+ alwaysClearLocalState = false
853
856
  }) {
854
- await withRetry({
855
- action: async () => {
856
- try {
857
- await api2.delete(
858
- `/api/v0/client-credentials/${credentials.clientId}`,
859
- void 0,
860
- { authRequired: true, requiredScopes: ["credentials"] }
861
- );
862
- } catch (err) {
863
- const status = getStatusCode(err);
864
- if (status === 404) return;
865
- if (status === 401) {
866
- console.warn(
867
- "Could not revoke credentials on the server (unauthorized). Local state will be cleared, but the credential may still be active. Verify or revoke via `list-client-credentials` or `delete-client-credentials`."
857
+ try {
858
+ await withRetry({
859
+ // Permanent errors (403/400) won't change on retry — fail fast instead of
860
+ // burning the backoff budget. The outer catch then rethrows (re-login) or
861
+ // swallows and clears local state (logout) per `alwaysClearLocalState`.
862
+ shouldRetry: (err) => !zapierSdk.isPermanentHttpError(err),
863
+ action: async () => {
864
+ try {
865
+ await api2.delete(
866
+ `/api/v0/client-credentials/${credentials.clientId}`,
867
+ void 0,
868
+ { authRequired: true, requiredScopes: ["credentials"] }
868
869
  );
869
- return;
870
+ } catch (err) {
871
+ const status = getStatusCode(err);
872
+ if (status === 404) return;
873
+ if (status === 401) {
874
+ console.warn(
875
+ "Could not revoke credentials on the server (unauthorized). " + REVOKE_WARNING_TAIL
876
+ );
877
+ return;
878
+ }
879
+ throw err;
870
880
  }
871
- throw err;
872
881
  }
882
+ });
883
+ } catch (err) {
884
+ if (!alwaysClearLocalState) throw err;
885
+ if (zapierSdk.isPermanentHttpError(err)) {
886
+ console.warn(REVOKE_REJECTED_WARNING);
887
+ } else {
888
+ console.warn(
889
+ "Could not revoke credentials on the server. " + REVOKE_WARNING_TAIL
890
+ );
873
891
  }
874
- });
892
+ }
875
893
  try {
876
894
  await deleteStoredClientCredentials({
877
895
  name: credentials.name,
@@ -1997,7 +2015,8 @@ var logoutPlugin = zapierSdk.definePlugin(
1997
2015
  await revokeCredentials({
1998
2016
  api: sdk2.context.api,
1999
2017
  credentials: activeCredentials,
2000
- onEvent
2018
+ onEvent,
2019
+ alwaysClearLocalState: true
2001
2020
  });
2002
2021
  console.log("\u2705 Successfully logged out");
2003
2022
  }
@@ -4505,7 +4524,7 @@ zapierSdk.definePlugin(
4505
4524
  // package.json with { type: 'json' }
4506
4525
  var package_default = {
4507
4526
  name: "@zapier/zapier-sdk-cli",
4508
- version: "0.54.1"};
4527
+ version: "0.54.2"};
4509
4528
 
4510
4529
  // src/sdk.ts
4511
4530
  zapierSdk.injectCliLogin(login_exports);
@@ -4533,7 +4552,7 @@ function createZapierCliSdk(options = {}) {
4533
4552
 
4534
4553
  // package.json
4535
4554
  var package_default2 = {
4536
- version: "0.54.1"};
4555
+ version: "0.54.2"};
4537
4556
 
4538
4557
  // src/telemetry/builders.ts
4539
4558
  function createCliBaseEvent(context = {}) {