@zapier/zapier-sdk-cli 0.54.1 → 0.54.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/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.3"};
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.3"};
4537
4556
 
4538
4557
  // src/telemetry/builders.ts
4539
4558
  function createCliBaseEvent(context = {}) {
package/dist/index.mjs CHANGED
@@ -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, injectCliLogin, getOrCreateApiClient, invalidateCachedToken, batch, toSnakeCase, ZapierAbortDrainSignal, isCredentialsObject, buildApplicationLifecycleEvent, ZapierAuthenticationError, ZapierError, createZapierSdkStack, addPlugin, getOsInfo, getPlatformVersions, getCiPlatform, isCi, getReleaseId, getCurrentTimestamp, generateEventId } from '@zapier/zapier-sdk';
10
+ import { definePlugin, createPluginMethod, OutputPropertySchema, ZapierBundleError, DEFAULT_CONFIG_PATH, ZapierValidationError, ZapierUnknownError, ZapierReleaseTriggerMessageSignal, injectCliLogin, getOrCreateApiClient, isPermanentHttpError, invalidateCachedToken, batch, toSnakeCase, ZapierAbortDrainSignal, isCredentialsObject, buildApplicationLifecycleEvent, ZapierAuthenticationError, ZapierError, createZapierSdkStack, addPlugin, getOsInfo, getPlatformVersions, getCiPlatform, isCi, getReleaseId, getCurrentTimestamp, generateEventId } from '@zapier/zapier-sdk';
11
11
  import { z } from 'zod';
12
12
  import { hostname } from 'os';
13
13
  import inquirer from 'inquirer';
@@ -777,7 +777,8 @@ function sleep(ms) {
777
777
  async function withRetry({
778
778
  action,
779
779
  attempts = 3,
780
- initialDelayMs = 100
780
+ initialDelayMs = 100,
781
+ shouldRetry = () => true
781
782
  }) {
782
783
  if (attempts <= 0) {
783
784
  throw new Error("withRetry: attempts must be greater than 0");
@@ -788,9 +789,8 @@ async function withRetry({
788
789
  return await action();
789
790
  } catch (err) {
790
791
  lastError = err;
791
- if (i < attempts - 1) {
792
- await sleep(initialDelayMs * 2 ** i);
793
- }
792
+ if (!shouldRetry(err) || i >= attempts - 1) break;
793
+ await sleep(initialDelayMs * 2 ** i);
794
794
  }
795
795
  }
796
796
  throw lastError;
@@ -810,32 +810,50 @@ function getStatusCode(err) {
810
810
  }
811
811
  return void 0;
812
812
  }
813
+ 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`.";
814
+ 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.";
813
815
  async function revokeCredentials({
814
816
  api: api2,
815
817
  credentials,
816
- onEvent
818
+ onEvent,
819
+ alwaysClearLocalState = false
817
820
  }) {
818
- await withRetry({
819
- action: async () => {
820
- try {
821
- await api2.delete(
822
- `/api/v0/client-credentials/${credentials.clientId}`,
823
- void 0,
824
- { authRequired: true, requiredScopes: ["credentials"] }
825
- );
826
- } catch (err) {
827
- const status = getStatusCode(err);
828
- if (status === 404) return;
829
- if (status === 401) {
830
- console.warn(
831
- "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`."
821
+ try {
822
+ await withRetry({
823
+ // Permanent errors (403/400) won't change on retry — fail fast instead of
824
+ // burning the backoff budget. The outer catch then rethrows (re-login) or
825
+ // swallows and clears local state (logout) per `alwaysClearLocalState`.
826
+ shouldRetry: (err) => !isPermanentHttpError(err),
827
+ action: async () => {
828
+ try {
829
+ await api2.delete(
830
+ `/api/v0/client-credentials/${credentials.clientId}`,
831
+ void 0,
832
+ { authRequired: true, requiredScopes: ["credentials"] }
832
833
  );
833
- return;
834
+ } catch (err) {
835
+ const status = getStatusCode(err);
836
+ if (status === 404) return;
837
+ if (status === 401) {
838
+ console.warn(
839
+ "Could not revoke credentials on the server (unauthorized). " + REVOKE_WARNING_TAIL
840
+ );
841
+ return;
842
+ }
843
+ throw err;
834
844
  }
835
- throw err;
836
845
  }
846
+ });
847
+ } catch (err) {
848
+ if (!alwaysClearLocalState) throw err;
849
+ if (isPermanentHttpError(err)) {
850
+ console.warn(REVOKE_REJECTED_WARNING);
851
+ } else {
852
+ console.warn(
853
+ "Could not revoke credentials on the server. " + REVOKE_WARNING_TAIL
854
+ );
837
855
  }
838
- });
856
+ }
839
857
  try {
840
858
  await deleteStoredClientCredentials({
841
859
  name: credentials.name,
@@ -1961,7 +1979,8 @@ var logoutPlugin = definePlugin(
1961
1979
  await revokeCredentials({
1962
1980
  api: sdk2.context.api,
1963
1981
  credentials: activeCredentials,
1964
- onEvent
1982
+ onEvent,
1983
+ alwaysClearLocalState: true
1965
1984
  });
1966
1985
  console.log("\u2705 Successfully logged out");
1967
1986
  }
@@ -4469,7 +4488,7 @@ definePlugin(
4469
4488
  // package.json with { type: 'json' }
4470
4489
  var package_default = {
4471
4490
  name: "@zapier/zapier-sdk-cli",
4472
- version: "0.54.1"};
4491
+ version: "0.54.3"};
4473
4492
 
4474
4493
  // src/sdk.ts
4475
4494
  injectCliLogin(login_exports);
@@ -4497,7 +4516,7 @@ function createZapierCliSdk(options = {}) {
4497
4516
 
4498
4517
  // package.json
4499
4518
  var package_default2 = {
4500
- version: "0.54.1"};
4519
+ version: "0.54.3"};
4501
4520
 
4502
4521
  // src/telemetry/builders.ts
4503
4522
  function createCliBaseEvent(context = {}) {
package/dist/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zapier/zapier-sdk-cli",
3
- "version": "0.54.1",
3
+ "version": "0.54.3",
4
4
  "description": "Command line interface for Zapier SDK",
5
5
  "main": "dist/index.cjs",
6
6
  "module": "dist/index.mjs",
@@ -6,8 +6,9 @@ export type LogoutEventEmitter = (event: {
6
6
  timestamp: number;
7
7
  }) => void;
8
8
  export declare function emitAuthLogout(onEvent: LogoutEventEmitter | undefined): void;
9
- export declare function revokeCredentials({ api, credentials, onEvent, }: {
9
+ export declare function revokeCredentials({ api, credentials, onEvent, alwaysClearLocalState, }: {
10
10
  api: ApiClient;
11
11
  credentials: CredentialsEntry;
12
12
  onEvent?: LogoutEventEmitter;
13
+ alwaysClearLocalState?: boolean;
13
14
  }): Promise<void>;
@@ -1,4 +1,4 @@
1
- import { invalidateCachedToken } from "@zapier/zapier-sdk";
1
+ import { invalidateCachedToken, isPermanentHttpError, } from "@zapier/zapier-sdk";
2
2
  import { clearLegacyJwtState } from "./legacy-jwt";
3
3
  import { deleteStoredClientCredentials, } from "./credentials-store";
4
4
  import { withRetry } from "../utils/retry";
@@ -15,27 +15,67 @@ function getStatusCode(err) {
15
15
  }
16
16
  return undefined;
17
17
  }
18
+ const REVOKE_WARNING_TAIL = "Local state will be cleared, but the credential may still be active. " +
19
+ "Verify or revoke via `list-client-credentials` or `delete-client-credentials`.";
20
+ // Shown only when `logout` tears down local state after the server *rejected*
21
+ // revocation (a permanent 4xx). Retrying won't help, and `list-`/`delete-client-
22
+ // credentials` would hit the same refusal until the user re-authenticates — so we
23
+ // send them through `login`, which restores the ability to manage credentials.
24
+ const REVOKE_REJECTED_WARNING = "Could not revoke the credential on the server: the request was rejected " +
25
+ "(your current credentials may lack permission to revoke it). Local state has " +
26
+ "been cleared, but the credential may still be active on the server — run " +
27
+ "`login` to re-authenticate, then `delete-client-credentials` to remove it.";
18
28
  // Re-login flows pass no emitter so credential rotation stays silent.
19
- export async function revokeCredentials({ api, credentials, onEvent, }) {
20
- await withRetry({
21
- action: async () => {
22
- try {
23
- await api.delete(`/api/v0/client-credentials/${credentials.clientId}`, undefined, { authRequired: true, requiredScopes: ["credentials"] });
24
- }
25
- catch (err) {
26
- const status = getStatusCode(err);
27
- if (status === 404)
28
- return;
29
- if (status === 401) {
30
- console.warn("Could not revoke credentials on the server (unauthorized). " +
31
- "Local state will be cleared, but the credential may still be active. " +
32
- "Verify or revoke via `list-client-credentials` or `delete-client-credentials`.");
33
- return;
29
+ export async function revokeCredentials({ api, credentials, onEvent, alwaysClearLocalState = false, }) {
30
+ try {
31
+ await withRetry({
32
+ // Permanent errors (403/400) won't change on retry — fail fast instead of
33
+ // burning the backoff budget. The outer catch then rethrows (re-login) or
34
+ // swallows and clears local state (logout) per `alwaysClearLocalState`.
35
+ shouldRetry: (err) => !isPermanentHttpError(err),
36
+ action: async () => {
37
+ try {
38
+ await api.delete(`/api/v0/client-credentials/${credentials.clientId}`, undefined, { authRequired: true, requiredScopes: ["credentials"] });
34
39
  }
35
- throw err;
36
- }
37
- },
38
- });
40
+ catch (err) {
41
+ const status = getStatusCode(err);
42
+ if (status === 404)
43
+ return;
44
+ if (status === 401) {
45
+ console.warn("Could not revoke credentials on the server (unauthorized). " +
46
+ REVOKE_WARNING_TAIL);
47
+ return;
48
+ }
49
+ throw err;
50
+ }
51
+ },
52
+ });
53
+ }
54
+ catch (err) {
55
+ // Re-login leaves `alwaysClearLocalState` false because it wants the throw:
56
+ // any revoke failure (permanent OR transient) must surface so it can confirm
57
+ // before replacing creds (see `account-auth.ts`). Only `logout` sets the flag
58
+ // — it is teardown, so it swallows the failure and clears local state rather
59
+ // than strand the user behind a revoke they can never complete.
60
+ if (!alwaysClearLocalState)
61
+ throw err;
62
+ if (isPermanentHttpError(err)) {
63
+ // A permanent client rejection (4xx except the 401/404 the action handles
64
+ // above — e.g. a 403 when the token lacks the `credentials` scope) is the
65
+ // server's definitive "you may not revoke this". Retrying won't help, and
66
+ // there's no dashboard fallback; `delete-client-credentials` needs the same
67
+ // refused `credentials` scope, so the message routes the user through `login`
68
+ // (which restores that scope) first, then `delete-client-credentials`.
69
+ console.warn(REVOKE_REJECTED_WARNING);
70
+ }
71
+ else {
72
+ // Transient 5xx/429, a network failure, or an auth-resolution throw that
73
+ // never built the request (e.g. a missing keychain secret): revocation never
74
+ // reached a verdict, so a later `delete-client-credentials` could still
75
+ // succeed once things recover.
76
+ console.warn("Could not revoke credentials on the server. " + REVOKE_WARNING_TAIL);
77
+ }
78
+ }
39
79
  try {
40
80
  await deleteStoredClientCredentials({
41
81
  name: credentials.name,
@@ -20,10 +20,13 @@ export const logoutPlugin = definePlugin((sdk) => createPluginMethod(sdk, {
20
20
  console.log("✅ Successfully logged out");
21
21
  return;
22
22
  }
23
+ // Logout is teardown: clear local state even if the server refuses to
24
+ // revoke the credential, so a permanent 4xx can't strand the user.
23
25
  await revokeCredentials({
24
26
  api: sdk.context.api,
25
27
  credentials: activeCredentials,
26
28
  onEvent,
29
+ alwaysClearLocalState: true,
27
30
  });
28
31
  console.log("✅ Successfully logged out");
29
32
  },
@@ -1,5 +1,6 @@
1
- export declare function withRetry<T>({ action, attempts, initialDelayMs, }: {
1
+ export declare function withRetry<T>({ action, attempts, initialDelayMs, shouldRetry, }: {
2
2
  action: () => Promise<T>;
3
3
  attempts?: number;
4
4
  initialDelayMs?: number;
5
+ shouldRetry?: (err: unknown) => boolean;
5
6
  }): Promise<T>;
@@ -1,7 +1,7 @@
1
1
  function sleep(ms) {
2
2
  return new Promise((resolve) => setTimeout(resolve, ms));
3
3
  }
4
- export async function withRetry({ action, attempts = 3, initialDelayMs = 100, }) {
4
+ export async function withRetry({ action, attempts = 3, initialDelayMs = 100, shouldRetry = () => true, }) {
5
5
  if (attempts <= 0) {
6
6
  throw new Error("withRetry: attempts must be greater than 0");
7
7
  }
@@ -12,9 +12,9 @@ export async function withRetry({ action, attempts = 3, initialDelayMs = 100, })
12
12
  }
13
13
  catch (err) {
14
14
  lastError = err;
15
- if (i < attempts - 1) {
16
- await sleep(initialDelayMs * 2 ** i);
17
- }
15
+ if (!shouldRetry(err) || i >= attempts - 1)
16
+ break;
17
+ await sleep(initialDelayMs * 2 ** i);
18
18
  }
19
19
  }
20
20
  throw lastError;