@zapier/zapier-sdk-cli 0.60.0 → 0.61.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/dist/cli.mjs CHANGED
@@ -59,12 +59,18 @@ var ZapierCliExitError = class extends ZapierCliError {
59
59
  this.exitCode = exitCode;
60
60
  }
61
61
  };
62
- var ZapierCliValidationError = class extends ZapierCliError {
63
- constructor(message) {
62
+ var ZapierCliValidationError = class _ZapierCliValidationError extends ZapierCliError {
63
+ constructor(message, options = {}) {
64
64
  super(message);
65
- this.name = "ZapierCliValidationError";
66
- this.code = "ZAPIER_CLI_VALIDATION_ERROR";
67
65
  this.exitCode = 1;
66
+ this.name = options.name ?? "ZapierCliValidationError";
67
+ this.code = options.code ?? "ZAPIER_CLI_VALIDATION_ERROR";
68
+ }
69
+ withMessage(message) {
70
+ return new _ZapierCliValidationError(message, {
71
+ name: this.name,
72
+ code: this.code
73
+ });
68
74
  }
69
75
  };
70
76
  var ZapierCliMissingParametersError = class extends ZapierCliError {
@@ -1536,7 +1542,7 @@ var SHARED_COMMAND_CLI_OPTIONS = [
1536
1542
 
1537
1543
  // package.json
1538
1544
  var package_default = {
1539
- version: "0.60.0"};
1545
+ version: "0.61.0"};
1540
1546
 
1541
1547
  // src/telemetry/builders.ts
1542
1548
  function createCliBaseEvent(context = {}) {
@@ -3722,41 +3728,165 @@ var spinPromise = async (promise, text) => {
3722
3728
  }
3723
3729
  };
3724
3730
 
3731
+ // src/utils/auth/oauth-errors.ts
3732
+ var OauthFlowTimeoutError = class _OauthFlowTimeoutError extends ZapierCliValidationError {
3733
+ constructor({
3734
+ timeoutMs,
3735
+ message = "OAuth flow timed out"
3736
+ }) {
3737
+ super(message, {
3738
+ name: "OauthFlowTimeoutError",
3739
+ code: "ZAPIER_OAUTH_FLOW_TIMEOUT"
3740
+ });
3741
+ this.timeoutMs = timeoutMs;
3742
+ }
3743
+ withMessage(message) {
3744
+ return new _OauthFlowTimeoutError({ timeoutMs: this.timeoutMs, message });
3745
+ }
3746
+ };
3747
+ var OauthAuthorizationDeniedError = class _OauthAuthorizationDeniedError extends ZapierCliValidationError {
3748
+ constructor({
3749
+ reason,
3750
+ message = "OAuth authorization denied"
3751
+ }) {
3752
+ super(message, {
3753
+ name: "OauthAuthorizationDeniedError",
3754
+ code: "ZAPIER_OAUTH_AUTHORIZATION_DENIED"
3755
+ });
3756
+ this.reason = reason;
3757
+ }
3758
+ withMessage(message) {
3759
+ return new _OauthAuthorizationDeniedError({
3760
+ reason: this.reason,
3761
+ message
3762
+ });
3763
+ }
3764
+ };
3765
+ var OauthFlowError = class _OauthFlowError extends ZapierCliValidationError {
3766
+ constructor({ message }) {
3767
+ super(message, {
3768
+ name: "OauthFlowError",
3769
+ code: "ZAPIER_OAUTH_FLOW"
3770
+ });
3771
+ }
3772
+ withMessage(message) {
3773
+ return new _OauthFlowError({ message });
3774
+ }
3775
+ };
3776
+ var OauthCallbackError = class _OauthCallbackError extends ZapierCliValidationError {
3777
+ constructor({ kind, message }) {
3778
+ super(message, {
3779
+ name: "OauthCallbackError",
3780
+ code: "ZAPIER_OAUTH_CALLBACK"
3781
+ });
3782
+ this.kind = kind;
3783
+ }
3784
+ withMessage(message) {
3785
+ return new _OauthCallbackError({ kind: this.kind, message });
3786
+ }
3787
+ };
3788
+ var OauthTokenExchangeError = class _OauthTokenExchangeError extends ZapierCliValidationError {
3789
+ constructor({ message }) {
3790
+ super(message, {
3791
+ name: "OauthTokenExchangeError",
3792
+ code: "ZAPIER_OAUTH_TOKEN_EXCHANGE"
3793
+ });
3794
+ }
3795
+ withMessage(message) {
3796
+ return new _OauthTokenExchangeError({ message });
3797
+ }
3798
+ };
3799
+ var SENSITIVE_OAUTH_FIELDS = [
3800
+ "access_token",
3801
+ "refresh_token",
3802
+ "id_token",
3803
+ "client_secret",
3804
+ "code_verifier",
3805
+ "code_challenge"
3806
+ ];
3807
+ function getErrorMessage(error) {
3808
+ return error instanceof Error ? error.message : String(error);
3809
+ }
3810
+ function toCamelCase(field) {
3811
+ return field.replace(
3812
+ /_([a-z])/g,
3813
+ (_match, letter) => letter.toUpperCase()
3814
+ );
3815
+ }
3816
+ function escapeRegExp(value) {
3817
+ return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
3818
+ }
3819
+ var sensitiveOauthFieldPattern = Array.from(
3820
+ new Set(
3821
+ SENSITIVE_OAUTH_FIELDS.flatMap((field) => [field, toCamelCase(field)])
3822
+ )
3823
+ ).map(escapeRegExp).join("|");
3824
+ var sensitiveQueryParamPattern = new RegExp(
3825
+ `([?&])(${sensitiveOauthFieldPattern})(=)[^&#\\s"'<>]*`,
3826
+ "gi"
3827
+ );
3828
+ function redactSensitiveOauthErrorMessage(message) {
3829
+ return message.replace(
3830
+ sensitiveQueryParamPattern,
3831
+ (_match, prefix, key, separator) => `${prefix}${key}${separator}[REDACTED]`
3832
+ ).replace(
3833
+ new RegExp(`"(${sensitiveOauthFieldPattern})"(\\s*:\\s*)"[^"]*"`, "g"),
3834
+ (_match, key, separator) => `"${key}"${separator}"[REDACTED]"`
3835
+ );
3836
+ }
3837
+ function toRedactedOauthError(error) {
3838
+ const message = redactSensitiveOauthErrorMessage(getErrorMessage(error));
3839
+ if (error instanceof ZapierCliValidationError) {
3840
+ return error.withMessage(message);
3841
+ }
3842
+ return new OauthFlowError({ message });
3843
+ }
3844
+ function toOauthTokenExchangeError(error) {
3845
+ return new OauthTokenExchangeError({
3846
+ message: redactSensitiveOauthErrorMessage(getErrorMessage(error))
3847
+ });
3848
+ }
3849
+
3725
3850
  // src/utils/auth/oauth-callback.ts
3726
3851
  function getCallbackCode({
3727
3852
  callbackUrl,
3728
- transaction,
3729
- recoveryMessage
3853
+ transaction
3730
3854
  }) {
3731
3855
  let parsed;
3732
3856
  try {
3733
3857
  parsed = new URL(callbackUrl.trim());
3734
3858
  } catch {
3735
- throw new ZapierCliValidationError(
3736
- "Paste the final OAuth callback URL from your browser."
3737
- );
3859
+ throw new OauthCallbackError({
3860
+ kind: "invalid_url",
3861
+ message: "Paste the final OAuth callback URL from your browser."
3862
+ });
3738
3863
  }
3739
3864
  const expected = new URL(transaction.redirectUri);
3740
3865
  if (parsed.protocol !== "http:" || parsed.hostname !== expected.hostname || parsed.pathname !== expected.pathname || parsed.port !== expected.port) {
3741
- throw new ZapierCliValidationError(
3742
- `Expected the final OAuth callback URL to start with ${transaction.redirectUri}.`
3743
- );
3866
+ throw new OauthCallbackError({
3867
+ kind: "redirect_mismatch",
3868
+ message: `Expected the final OAuth callback URL to start with ${transaction.redirectUri}.`
3869
+ });
3744
3870
  }
3745
3871
  if (parsed.searchParams.get("state") !== transaction.state) {
3746
- throw new ZapierCliValidationError(
3747
- `OAuth state mismatch.${recoveryMessage ? ` ${recoveryMessage}` : ""}`
3748
- );
3872
+ throw new OauthCallbackError({
3873
+ kind: "state_mismatch",
3874
+ message: "OAuth state mismatch."
3875
+ });
3749
3876
  }
3750
3877
  if (parsed.searchParams.has("error")) {
3751
- throw new ZapierCliValidationError(
3752
- `Authorization denied: ${parsed.searchParams.get("error_description") ?? parsed.searchParams.get("error")}.${recoveryMessage ? ` ${recoveryMessage}` : ""}`
3753
- );
3878
+ throw new OauthAuthorizationDeniedError({
3879
+ reason: String(
3880
+ parsed.searchParams.get("error_description") ?? parsed.searchParams.get("error")
3881
+ )
3882
+ });
3754
3883
  }
3755
3884
  const code = parsed.searchParams.get("code");
3756
3885
  if (!code) {
3757
- throw new ZapierCliValidationError(
3758
- "No authorization code found in the pasted callback URL."
3759
- );
3886
+ throw new OauthCallbackError({
3887
+ kind: "missing_code",
3888
+ message: "No authorization code found in the pasted callback URL."
3889
+ });
3760
3890
  }
3761
3891
  return code;
3762
3892
  }
@@ -3871,7 +4001,9 @@ async function exchangeOauthCode({
3871
4001
  "Content-Type": "application/x-www-form-urlencoded"
3872
4002
  }
3873
4003
  }
3874
- );
4004
+ ).catch((error) => {
4005
+ throw toOauthTokenExchangeError(error);
4006
+ });
3875
4007
  return {
3876
4008
  accessToken: data.access_token,
3877
4009
  refreshToken: data.refresh_token,
@@ -3880,19 +4012,15 @@ async function exchangeOauthCode({
3880
4012
  }
3881
4013
 
3882
4014
  // src/utils/auth/oauth-flow.ts
3883
- var OauthFlowTimeoutError = class extends Error {
3884
- constructor(timeoutMs) {
3885
- super("OAuth flow timed out");
3886
- this.timeoutMs = timeoutMs;
3887
- this.name = "OauthFlowTimeoutError";
3888
- }
4015
+ var LOGIN_OAUTH_COPY = {
4016
+ flowName: "Login",
4017
+ action: "log in",
4018
+ urlLabel: "login URL"
3889
4019
  };
3890
- var OauthAuthorizationDeniedError = class extends Error {
3891
- constructor(reason) {
3892
- super("OAuth authorization denied");
3893
- this.reason = reason;
3894
- this.name = "OauthAuthorizationDeniedError";
3895
- }
4020
+ var SIGNUP_OAUTH_COPY = {
4021
+ flowName: "Signup",
4022
+ action: "sign up",
4023
+ urlLabel: "signup URL"
3896
4024
  };
3897
4025
  function findAvailablePort() {
3898
4026
  return new Promise((resolve4, reject) => {
@@ -3907,70 +4035,86 @@ function findAvailablePort() {
3907
4035
  tryPort(LOGIN_PORTS[portIndex++]);
3908
4036
  } else if (err.code === "EADDRINUSE") {
3909
4037
  reject(
3910
- new Error(
3911
- `All configured OAuth callback ports are busy: ${LOGIN_PORTS.join(", ")}. Please try again later or close applications using these ports.`
3912
- )
4038
+ new OauthFlowError({
4039
+ message: `All configured OAuth callback ports are busy: ${LOGIN_PORTS.join(", ")}. Please try again later or close applications using these ports.`
4040
+ })
3913
4041
  );
3914
4042
  } else {
3915
4043
  reject(err);
3916
4044
  }
3917
4045
  });
3918
4046
  };
3919
- if (LOGIN_PORTS.length > 0) tryPort(LOGIN_PORTS[portIndex++]);
3920
- else reject(new Error("No OAuth callback ports configured"));
4047
+ if (LOGIN_PORTS.length > 0) {
4048
+ tryPort(LOGIN_PORTS[portIndex++]);
4049
+ return;
4050
+ }
4051
+ reject(
4052
+ new OauthFlowError({ message: "No OAuth callback ports configured" })
4053
+ );
3921
4054
  });
3922
4055
  }
3923
4056
  async function runLoginOauthFlow(options) {
3924
- return runOauthFlowEntryPoint({
3925
- ...options,
4057
+ return runOauthFlowForEntryPoint({
4058
+ options,
3926
4059
  entryPoint: "login",
3927
- authAction: "log in",
3928
- flowName: "Login"
4060
+ copy: LOGIN_OAUTH_COPY
3929
4061
  });
3930
4062
  }
3931
4063
  async function runSignupOauthFlow(options) {
4064
+ return runOauthFlowForEntryPoint({
4065
+ options,
4066
+ entryPoint: "signup",
4067
+ copy: SIGNUP_OAUTH_COPY
4068
+ });
4069
+ }
4070
+ function runOauthFlowForEntryPoint({
4071
+ options,
4072
+ entryPoint,
4073
+ copy
4074
+ }) {
3932
4075
  if (options.headless) {
3933
4076
  return runOauthFlowEntryPoint({
3934
4077
  ...options,
3935
- entryPoint: "signup",
3936
- authAction: "sign up",
3937
- flowName: "Signup",
3938
- headless: true
4078
+ entryPoint,
4079
+ headless: true,
4080
+ copy
3939
4081
  });
3940
4082
  }
3941
4083
  return runOauthFlowEntryPoint({
3942
4084
  ...options,
3943
- entryPoint: "signup",
3944
- authAction: "sign up",
3945
- flowName: "Signup"
4085
+ entryPoint,
4086
+ copy,
4087
+ headless: false
3946
4088
  });
3947
4089
  }
3948
- async function runOauthFlowEntryPoint({
3949
- flowName,
3950
- ...options
3951
- }) {
4090
+ async function runOauthFlowEntryPoint(options) {
3952
4091
  try {
3953
- return options.headless ? await runHeadlessSignupOauthFlow(options) : await runOauthFlow(options);
4092
+ return options.headless ? await runHeadlessOauthFlow(options) : await runOauthFlow(options);
3954
4093
  } catch (error) {
3955
4094
  if (error instanceof OauthFlowTimeoutError) {
3956
- throw new Error(
4095
+ throw error.withMessage(
3957
4096
  withRecoveryMessage(
3958
- `${flowName} timed out after ${Math.round(error.timeoutMs / 1e3)} seconds.`,
4097
+ `${options.copy.flowName} timed out after ${Math.round(error.timeoutMs / 1e3)} seconds.`,
3959
4098
  options.recoveryMessage
3960
4099
  )
3961
4100
  );
3962
4101
  }
3963
4102
  if (error instanceof OauthAuthorizationDeniedError) {
3964
- throw new Error(
4103
+ throw error.withMessage(
3965
4104
  withRecoveryMessage(
3966
4105
  `Authorization denied: ${error.reason}.`,
3967
4106
  options.recoveryMessage
3968
4107
  )
3969
4108
  );
3970
4109
  }
4110
+ if (error instanceof OauthCallbackError && options.recoveryMessage) {
4111
+ throw error.withMessage(
4112
+ withRecoveryMessage(error.message, options.recoveryMessage)
4113
+ );
4114
+ }
3971
4115
  if (error instanceof ZapierCliUserCancellationError && !options.silent) {
3972
4116
  log_default.info(`
3973
- \u274C ${flowName} cancelled by user`);
4117
+ \u274C ${options.copy.flowName} cancelled by user`);
3974
4118
  }
3975
4119
  throw error;
3976
4120
  }
@@ -3978,12 +4122,22 @@ async function runOauthFlowEntryPoint({
3978
4122
  function withRecoveryMessage(message, recoveryMessage) {
3979
4123
  return recoveryMessage ? `${message} ${recoveryMessage}` : message;
3980
4124
  }
4125
+ function createMissingHeadlessCallbackUrlError() {
4126
+ return new OauthCallbackError({
4127
+ kind: "missing_callback_url",
4128
+ message: "Paste the final OAuth callback URL from your browser."
4129
+ });
4130
+ }
4131
+ function writeHeadlessOauthStatus(message) {
4132
+ process.stderr.write(`${message}
4133
+ `);
4134
+ }
3981
4135
  async function runOauthFlow({
3982
4136
  timeoutMs = LOGIN_TIMEOUT_MS,
3983
4137
  pkceCredentials,
3984
4138
  baseUrl: baseUrl2,
3985
4139
  entryPoint,
3986
- authAction,
4140
+ copy,
3987
4141
  silent = false,
3988
4142
  onProgress
3989
4143
  }) {
@@ -3998,7 +4152,7 @@ async function runOauthFlow({
3998
4152
  const code = await collectLocalCallbackCode({
3999
4153
  transaction,
4000
4154
  timeoutMs,
4001
- authAction,
4155
+ action: copy.action,
4002
4156
  silent,
4003
4157
  onProgress
4004
4158
  });
@@ -4012,92 +4166,104 @@ async function runOauthFlow({
4012
4166
  }
4013
4167
  async function readHeadlessCallbackUrl({
4014
4168
  timeoutMs,
4015
- interactive,
4016
- recoveryMessage
4169
+ interactive
4017
4170
  }) {
4018
- const timeoutMessage = withRecoveryMessage(
4019
- `Signup timed out after ${Math.round(timeoutMs / 1e3)} seconds.`,
4020
- recoveryMessage
4021
- );
4022
- const missingCallbackUrlMessage = withRecoveryMessage(
4023
- "Paste the final OAuth callback URL from your browser.",
4024
- recoveryMessage
4025
- );
4026
4171
  const rl = createInterface({ input: process.stdin, output: process.stderr });
4027
4172
  const abortController = new AbortController();
4028
4173
  const timeoutTimer = setTimeout(() => abortController.abort(), timeoutMs);
4029
- const readUrl = interactive ? rl.question("Paste the final OAuth callback URL: ", {
4030
- signal: abortController.signal
4031
- }) : new Promise((resolve4, reject) => {
4174
+ const readUrl = new Promise((resolve4, reject) => {
4032
4175
  let settled = false;
4033
- const settleResolve = (value) => {
4176
+ const handleLine = (value) => settleResolve(value);
4177
+ const handleAbort = () => settleReject(new OauthFlowTimeoutError({ timeoutMs }));
4178
+ const handleClose = () => settleReject(createMissingHeadlessCallbackUrlError());
4179
+ const handleError = (error) => settleReject(error);
4180
+ const handleCancellation = () => settleReject(new ZapierCliUserCancellationError());
4181
+ const cleanupListeners = () => {
4182
+ abortController.signal.removeEventListener("abort", handleAbort);
4183
+ rl.off("line", handleLine);
4184
+ rl.off("close", handleClose);
4185
+ rl.off("error", handleError);
4186
+ rl.off("SIGINT", handleCancellation);
4187
+ process.off("SIGINT", handleCancellation);
4188
+ process.off("SIGTERM", handleCancellation);
4189
+ };
4190
+ function settleResolve(value) {
4191
+ if (settled) return;
4034
4192
  settled = true;
4193
+ cleanupListeners();
4035
4194
  resolve4(value);
4036
- };
4037
- const settleReject = (error) => {
4195
+ }
4196
+ function settleReject(error) {
4038
4197
  if (settled) return;
4039
4198
  settled = true;
4199
+ cleanupListeners();
4040
4200
  reject(error);
4041
- };
4042
- abortController.signal.addEventListener(
4043
- "abort",
4044
- () => settleReject(new Error(timeoutMessage)),
4045
- { once: true }
4046
- );
4047
- rl.once("line", settleResolve);
4048
- rl.once(
4049
- "close",
4050
- () => settleReject(new ZapierCliValidationError(missingCallbackUrlMessage))
4051
- );
4052
- rl.once("error", settleReject);
4201
+ }
4202
+ abortController.signal.addEventListener("abort", handleAbort, {
4203
+ once: true
4204
+ });
4205
+ rl.once("close", handleClose);
4206
+ rl.once("error", handleError);
4207
+ rl.once("SIGINT", handleCancellation);
4208
+ process.once("SIGINT", handleCancellation);
4209
+ process.once("SIGTERM", handleCancellation);
4210
+ if (interactive) {
4211
+ void rl.question("Paste the final OAuth callback URL: ", {
4212
+ signal: abortController.signal
4213
+ }).then(settleResolve).catch((error) => {
4214
+ if (error instanceof Error && error.name === "AbortError") {
4215
+ settleReject(new OauthFlowTimeoutError({ timeoutMs }));
4216
+ return;
4217
+ }
4218
+ settleReject(error);
4219
+ });
4220
+ return;
4221
+ }
4222
+ rl.once("line", handleLine);
4053
4223
  });
4054
4224
  try {
4055
- return await readUrl.catch((error) => {
4056
- if (error instanceof Error && error.name === "AbortError") {
4057
- throw new Error(timeoutMessage);
4058
- }
4059
- throw error;
4060
- });
4225
+ return await readUrl;
4061
4226
  } finally {
4062
4227
  clearTimeout(timeoutTimer);
4063
4228
  rl.close();
4064
4229
  }
4065
4230
  }
4066
- async function runHeadlessSignupOauthFlow({
4231
+ async function runHeadlessOauthFlow({
4067
4232
  timeoutMs = LOGIN_TIMEOUT_MS,
4068
4233
  pkceCredentials,
4069
4234
  baseUrl: baseUrl2,
4235
+ entryPoint,
4070
4236
  interactive = true,
4071
4237
  onProgress,
4072
- recoveryMessage
4238
+ copy
4073
4239
  }) {
4074
4240
  const port = LOGIN_PORTS[0];
4075
4241
  const transaction = await prepareOauthTransaction({
4076
4242
  pkceCredentials,
4077
4243
  baseUrl: baseUrl2,
4078
4244
  redirectUri: `http://${OAUTH_LOOPBACK_HOST}:${port}/oauth`,
4079
- entryPoint: "signup"
4245
+ entryPoint
4080
4246
  });
4081
- console.log(
4082
- "Use this mode when signing up from a machine that has no browser."
4247
+ writeHeadlessOauthStatus(
4248
+ `Use this mode to ${copy.action} from a machine that has no browser.`
4249
+ );
4250
+ writeHeadlessOauthStatus(
4251
+ `Open this ${copy.urlLabel} in a browser on another machine:`
4083
4252
  );
4084
- console.log("Open this signup URL in a browser on another machine:");
4085
4253
  console.log(transaction.browserAuthUrl);
4086
- console.log(
4254
+ writeHeadlessOauthStatus(
4087
4255
  `When the browser lands on ${transaction.redirectUri} and cannot connect, paste the full final URL back here.`
4088
4256
  );
4089
4257
  const callbackUrl = await readHeadlessCallbackUrl({
4090
4258
  timeoutMs,
4091
- interactive,
4092
- recoveryMessage
4259
+ interactive
4093
4260
  });
4094
4261
  const code = getCallbackCode({
4095
4262
  callbackUrl,
4096
- transaction,
4097
- recoveryMessage
4263
+ transaction
4098
4264
  });
4099
4265
  onProgress?.({ type: "callback_accepted" });
4100
- console.log("Exchanging authorization code for tokens...");
4266
+ writeHeadlessOauthStatus("Exchanging authorization code for tokens...");
4101
4267
  onProgress?.({ type: "token_exchange_started" });
4102
4268
  const tokens = await exchangeOauthCode({ ...transaction, code });
4103
4269
  onProgress?.({ type: "token_exchange_completed" });
@@ -4106,7 +4272,7 @@ async function runHeadlessSignupOauthFlow({
4106
4272
  async function collectLocalCallbackCode({
4107
4273
  transaction,
4108
4274
  timeoutMs,
4109
- authAction,
4275
+ action,
4110
4276
  silent,
4111
4277
  onProgress
4112
4278
  }) {
@@ -4114,21 +4280,26 @@ async function collectLocalCallbackCode({
4114
4280
  const app = express();
4115
4281
  app.get("/oauth", (req, res) => {
4116
4282
  res.setHeader("Connection", "close");
4117
- if (req.query.state !== transaction.state) {
4118
- res.status(400).end("Invalid state. You can close this tab.");
4119
- } else if (req.query.error) {
4120
- reject(
4121
- new OauthAuthorizationDeniedError(
4122
- String(req.query.error_description ?? req.query.error)
4123
- )
4124
- );
4125
- res.end("Authorization was denied. You can close this tab.");
4126
- } else if (!req.query.code) {
4127
- reject(new Error("No authorization code received"));
4128
- res.end("No authorization code received. You can close this tab.");
4129
- } else {
4130
- resolve4(String(req.query.code));
4283
+ try {
4284
+ const code = getCallbackCode({
4285
+ callbackUrl: new URL(
4286
+ req.originalUrl,
4287
+ transaction.redirectUri
4288
+ ).toString(),
4289
+ transaction
4290
+ });
4291
+ resolve4(code);
4131
4292
  res.end("You can now close this tab and return to the CLI.");
4293
+ } catch (error) {
4294
+ if (error instanceof OauthCallbackError && error.kind === "state_mismatch") {
4295
+ res.status(400).end("Invalid state. You can close this tab.");
4296
+ } else if (error instanceof OauthAuthorizationDeniedError) {
4297
+ reject(error);
4298
+ res.end("Authorization was denied. You can close this tab.");
4299
+ } else {
4300
+ reject(error);
4301
+ res.end("No authorization code received. You can close this tab.");
4302
+ }
4132
4303
  }
4133
4304
  });
4134
4305
  const server = app.listen(
@@ -4149,19 +4320,19 @@ async function collectLocalCallbackCode({
4149
4320
  let timeoutTimer;
4150
4321
  try {
4151
4322
  await waitForServerListening(server);
4152
- await openBrowser({ transaction, authAction, silent, onProgress });
4323
+ await openBrowser({ transaction, action, silent, onProgress });
4153
4324
  const waitForCode = Promise.race([
4154
4325
  promise,
4155
4326
  new Promise((_resolve, rejectTimeout) => {
4156
4327
  timeoutTimer = setTimeout(() => {
4157
- rejectTimeout(new OauthFlowTimeoutError(timeoutMs));
4328
+ rejectTimeout(new OauthFlowTimeoutError({ timeoutMs }));
4158
4329
  }, timeoutMs);
4159
4330
  })
4160
4331
  ]);
4161
4332
  onProgress?.({ type: "callback_waiting" });
4162
4333
  return silent ? await waitForCode : await spinPromise(
4163
4334
  waitForCode,
4164
- `Waiting for you to ${authAction} and authorize`
4335
+ `Waiting for you to ${action} and authorize`
4165
4336
  );
4166
4337
  } finally {
4167
4338
  if (timeoutTimer) clearTimeout(timeoutTimer);
@@ -4191,12 +4362,12 @@ async function waitForServerListening(server) {
4191
4362
  }
4192
4363
  async function openBrowser({
4193
4364
  transaction,
4194
- authAction,
4365
+ action,
4195
4366
  silent,
4196
4367
  onProgress
4197
4368
  }) {
4198
4369
  if (!silent) {
4199
- log_default.info(`Opening your browser to ${authAction}.`);
4370
+ log_default.info(`Opening your browser to ${action}.`);
4200
4371
  log_default.info("If it doesn't open, visit:", transaction.browserAuthUrl);
4201
4372
  }
4202
4373
  onProgress?.({ type: "browser_opening", url: transaction.browserAuthUrl });
@@ -4206,9 +4377,7 @@ async function openBrowser({
4206
4377
  } catch (err) {
4207
4378
  const reason = err instanceof Error ? err.message : String(err);
4208
4379
  if (!silent) {
4209
- log_default.info(
4210
- `Browser did not open automatically to ${authAction}: ${reason}`
4211
- );
4380
+ log_default.info(`Browser did not open automatically to ${action}: ${reason}`);
4212
4381
  log_default.info("Visit this URL manually:", transaction.browserAuthUrl);
4213
4382
  }
4214
4383
  onProgress?.({
@@ -4237,61 +4406,10 @@ async function closeServer({
4237
4406
  });
4238
4407
  }
4239
4408
 
4240
- // src/utils/auth/oauth-errors.ts
4241
- var SENSITIVE_OAUTH_FIELDS = [
4242
- "access_token",
4243
- "refresh_token",
4244
- "id_token",
4245
- "client_secret",
4246
- "code_verifier",
4247
- "code_challenge"
4248
- ];
4249
- function getErrorMessage(error) {
4250
- return error instanceof Error ? error.message : String(error);
4251
- }
4252
- function toCamelCase(field) {
4253
- return field.replace(
4254
- /_([a-z])/g,
4255
- (_match, letter) => letter.toUpperCase()
4256
- );
4257
- }
4258
- function escapeRegExp(value) {
4259
- return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
4260
- }
4261
- var sensitiveOauthFieldPattern = Array.from(
4262
- new Set(
4263
- SENSITIVE_OAUTH_FIELDS.flatMap((field) => [field, toCamelCase(field)])
4264
- )
4265
- ).map(escapeRegExp).join("|");
4266
- var sensitiveQueryParamPattern = new RegExp(
4267
- `([?&])(${sensitiveOauthFieldPattern})(=)[^&#\\s"'<>]*`,
4268
- "gi"
4269
- );
4270
- function redactSensitiveOauthErrorMessage(message) {
4271
- return message.replace(
4272
- sensitiveQueryParamPattern,
4273
- (_match, prefix, key, separator) => `${prefix}${key}${separator}[REDACTED]`
4274
- ).replace(
4275
- new RegExp(`"(${sensitiveOauthFieldPattern})"(\\s*:\\s*)"[^"]*"`, "g"),
4276
- (_match, key, separator) => `"${key}"${separator}"[REDACTED]"`
4277
- );
4278
- }
4279
- function toRedactedOauthError(error) {
4280
- const message = redactSensitiveOauthErrorMessage(getErrorMessage(error));
4281
- if (error instanceof ZapierCliValidationError) {
4282
- return new ZapierCliValidationError(message);
4283
- }
4284
- if (error instanceof Error) {
4285
- const redactedError = new Error(message);
4286
- redactedError.name = error.name;
4287
- return redactedError;
4288
- }
4289
- return new ZapierCliValidationError(message);
4290
- }
4291
-
4292
4409
  // src/utils/auth/account-auth.ts
4293
4410
  var LEGACY_JWT_UPGRADE_PROMPT = "We're upgrading your login to client credentials for a simpler, more reliable experience and to support future security controls. Older Zapier SDK/CLI versions on this machine may stop working after the upgrade. Continue?";
4294
4411
  var SIGNUP_RECOVERY_MESSAGE = "Restart `zapier-sdk signup` to generate a fresh signup URL and try again.";
4412
+ var HEADLESS_LOGIN_RECOVERY_MESSAGE = "Restart `zapier-sdk login --headless` to generate a fresh login URL and try again.";
4295
4413
  var HEADLESS_SIGNUP_RECOVERY_MESSAGE = "Restart `zapier-sdk signup --headless` to generate a fresh signup URL and try again.";
4296
4414
  function getEntryPointLabel(entryPoint) {
4297
4415
  return entryPoint === "signup" ? "Signup" : "Login";
@@ -4411,7 +4529,8 @@ Logging out will delete these credentials and may interrupt other Zapier SDK or
4411
4529
  Log out and ${getActiveCredentialsAction(entryPoint)}?`
4412
4530
  }) : promptlessCredentialResetError(activeCredentials);
4413
4531
  if (!confirmed) {
4414
- console.log(`${flowLabel} cancelled.`);
4532
+ process.stderr.write(`${flowLabel} cancelled.
4533
+ `);
4415
4534
  return false;
4416
4535
  }
4417
4536
  try {
@@ -4430,7 +4549,8 @@ Log out and ${getActiveCredentialsAction(entryPoint)}?`
4430
4549
  message: `${flowLabel} cleanup failed. Reset local session state and continue?`
4431
4550
  });
4432
4551
  if (!reset) {
4433
- console.log(`${flowLabel} cancelled.`);
4552
+ process.stderr.write(`${flowLabel} cancelled.
4553
+ `);
4434
4554
  return false;
4435
4555
  }
4436
4556
  await deleteStoredClientCredentials({
@@ -4444,7 +4564,8 @@ Log out and ${getActiveCredentialsAction(entryPoint)}?`
4444
4564
  message: LEGACY_JWT_UPGRADE_PROMPT
4445
4565
  }) : promptlessLegacyJwtUpgradeError();
4446
4566
  if (!confirmed) {
4447
- console.log(`${flowLabel} cancelled.`);
4567
+ process.stderr.write(`${flowLabel} cancelled.
4568
+ `);
4448
4569
  return false;
4449
4570
  }
4450
4571
  }
@@ -4539,7 +4660,14 @@ async function runOauthForEntryPoint({
4539
4660
  );
4540
4661
  }
4541
4662
  return runOauthWithRedaction(
4542
- () => runLoginOauthFlow({ timeoutMs, pkceCredentials, baseUrl: baseUrl2 })
4663
+ () => runLoginOauthFlow({
4664
+ timeoutMs,
4665
+ pkceCredentials,
4666
+ baseUrl: baseUrl2,
4667
+ headless,
4668
+ interactive,
4669
+ recoveryMessage: headless ? HEADLESS_LOGIN_RECOVERY_MESSAGE : void 0
4670
+ })
4543
4671
  );
4544
4672
  }
4545
4673
  async function runAccountAuth({
@@ -4551,6 +4679,7 @@ async function runAccountAuth({
4551
4679
  const interactive = !resolveNonInteractive(options);
4552
4680
  const resolvedCredentials = await sdk.context.resolveCredentials();
4553
4681
  const pkceCredentials = toPkceCredentials(resolvedCredentials);
4682
+ const headless = options.headless === true;
4554
4683
  const credentialsBaseUrl2 = await resolveCredentialsBaseUrl({
4555
4684
  ...sdk.context,
4556
4685
  resolvedCredentials
@@ -4580,7 +4709,7 @@ async function runAccountAuth({
4580
4709
  timeoutMs: timeoutSeconds * 1e3,
4581
4710
  pkceCredentials,
4582
4711
  baseUrl: credentialsBaseUrl2,
4583
- headless: options.headless === true,
4712
+ headless,
4584
4713
  interactive
4585
4714
  });
4586
4715
  const scopedApi = getOrCreateApiClient({
@@ -4588,9 +4717,10 @@ async function runAccountAuth({
4588
4717
  baseUrl: credentialsBaseUrl2
4589
4718
  });
4590
4719
  const profile = await getProfile(scopedApi);
4591
- console.log(getProfileMessage(entryPoint, profile.email));
4592
- console.log(
4593
- "\nGenerating credentials so this machine can make authenticated requests on your behalf."
4720
+ process.stderr.write(`${getProfileMessage(entryPoint, profile.email)}
4721
+ `);
4722
+ process.stderr.write(
4723
+ "\nGenerating credentials so this machine can make authenticated requests on your behalf.\n"
4594
4724
  );
4595
4725
  const credentialName = providedName ?? await resolveCredentialName({
4596
4726
  email: profile.email,
@@ -4606,11 +4736,12 @@ async function runAccountAuth({
4606
4736
  useApprovals,
4607
4737
  cleanupLogPrefix: entryPoint
4608
4738
  });
4609
- console.log(
4610
- `\u2705 Credentials "${credentialName}" created and set as default. You are ready to use the Zapier SDK.`
4739
+ process.stderr.write(
4740
+ `\u2705 Credentials "${credentialName}" created and set as default. You are ready to use the Zapier SDK.
4741
+ `
4611
4742
  );
4612
4743
  if (useApprovals) {
4613
- console.log("\u{1F510} Approvals are enabled for these credentials.");
4744
+ process.stderr.write("\u{1F510} Approvals are enabled for these credentials.\n");
4614
4745
  }
4615
4746
  emitAccountAuthSuccess({ sdk, profile, clientId });
4616
4747
  }
@@ -4629,7 +4760,10 @@ var LoginSchema = z.object({
4629
4760
  skipPrompts: z.boolean().optional().meta({
4630
4761
  deprecated: true,
4631
4762
  deprecationMessage: "Use --non-interactive instead."
4632
- })
4763
+ }),
4764
+ headless: z.boolean().optional().describe(
4765
+ "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."
4766
+ )
4633
4767
  }).describe("Log in to Zapier to access your account");
4634
4768
 
4635
4769
  // src/plugins/login/index.ts
@@ -7261,7 +7395,7 @@ function buildBoxLines(message) {
7261
7395
  // package.json with { type: 'json' }
7262
7396
  var package_default2 = {
7263
7397
  name: "@zapier/zapier-sdk-cli",
7264
- version: "0.60.0"};
7398
+ version: "0.61.0"};
7265
7399
 
7266
7400
  // src/sdk.ts
7267
7401
  injectCliLogin(login_exports);