@zapier/zapier-sdk-cli 0.52.12 → 0.53.1
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/AGENTS.md +326 -0
- package/CHANGELOG.md +22 -0
- package/CLAUDE.md +3 -324
- package/README.md +20 -0
- package/dist/cli.cjs +896 -395
- package/dist/cli.mjs +897 -396
- package/dist/experimental.cjs +897 -398
- package/dist/experimental.d.mts +1 -1
- package/dist/experimental.d.ts +1 -1
- package/dist/experimental.mjs +896 -397
- package/dist/index.cjs +898 -399
- package/dist/index.d.mts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.mjs +897 -398
- package/dist/package.json +2 -1
- package/dist/{sdk-Sa1HjzUj.d.mts → sdk-SOLizjno.d.mts} +40 -2
- package/dist/{sdk-Sa1HjzUj.d.ts → sdk-SOLizjno.d.ts} +40 -2
- package/dist/src/experimental.js +2 -1
- package/dist/src/plugins/index.d.ts +1 -0
- package/dist/src/plugins/index.js +1 -0
- package/dist/src/plugins/login/index.d.ts +2 -15
- package/dist/src/plugins/login/index.js +3 -191
- package/dist/src/plugins/signup/index.d.ts +25 -0
- package/dist/src/plugins/signup/index.js +12 -0
- package/dist/src/plugins/signup/schemas.d.ts +9 -0
- package/dist/src/plugins/signup/schemas.js +26 -0
- package/dist/src/plugins/signup/test-harness.d.ts +34 -0
- package/dist/src/plugins/signup/test-harness.js +74 -0
- package/dist/src/sdk.js +2 -1
- package/dist/src/types/sdk.d.ts +2 -1
- package/dist/src/utils/auth/account-auth.d.ts +32 -0
- package/dist/src/utils/auth/account-auth.js +265 -0
- package/dist/src/utils/auth/oauth-callback.d.ts +6 -0
- package/dist/src/utils/auth/oauth-callback.js +28 -0
- package/dist/src/utils/auth/oauth-errors.d.ts +2 -0
- package/dist/src/utils/auth/oauth-errors.js +39 -0
- package/dist/src/utils/auth/oauth-flow.d.ts +31 -6
- package/dist/src/utils/auth/oauth-flow.js +258 -106
- package/dist/src/utils/auth/oauth-transaction.d.ts +35 -0
- package/dist/src/utils/auth/oauth-transaction.js +69 -0
- package/dist/src/utils/non-interactive.d.ts +5 -4
- package/dist/src/utils/non-interactive.js +6 -5
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +4 -3
- package/templates/basic/AGENTS.md.hbs +2 -2
package/dist/experimental.cjs
CHANGED
|
@@ -12,11 +12,12 @@ var zod = require('zod');
|
|
|
12
12
|
var experimental = require('@zapier/zapier-sdk/experimental');
|
|
13
13
|
var os = require('os');
|
|
14
14
|
var inquirer = require('inquirer');
|
|
15
|
-
var open = require('open');
|
|
16
15
|
var express = require('express');
|
|
17
|
-
var
|
|
18
|
-
var
|
|
16
|
+
var promises$1 = require('readline/promises');
|
|
17
|
+
var open = require('open');
|
|
19
18
|
var chalk3 = require('chalk');
|
|
19
|
+
var ora = require('ora');
|
|
20
|
+
var pkceChallenge = require('pkce-challenge');
|
|
20
21
|
var zapierSdkMcp = require('@zapier/zapier-sdk-mcp');
|
|
21
22
|
var esbuild = require('esbuild');
|
|
22
23
|
var promises = require('fs/promises');
|
|
@@ -55,11 +56,11 @@ var crypto__default = /*#__PURE__*/_interopDefault(crypto);
|
|
|
55
56
|
var path__namespace = /*#__PURE__*/_interopNamespace(path);
|
|
56
57
|
var lockfile__namespace = /*#__PURE__*/_interopNamespace(lockfile);
|
|
57
58
|
var inquirer__default = /*#__PURE__*/_interopDefault(inquirer);
|
|
58
|
-
var open__default = /*#__PURE__*/_interopDefault(open);
|
|
59
59
|
var express__default = /*#__PURE__*/_interopDefault(express);
|
|
60
|
-
var
|
|
61
|
-
var ora__default = /*#__PURE__*/_interopDefault(ora);
|
|
60
|
+
var open__default = /*#__PURE__*/_interopDefault(open);
|
|
62
61
|
var chalk3__default = /*#__PURE__*/_interopDefault(chalk3);
|
|
62
|
+
var ora__default = /*#__PURE__*/_interopDefault(ora);
|
|
63
|
+
var pkceChallenge__default = /*#__PURE__*/_interopDefault(pkceChallenge);
|
|
63
64
|
var ts__namespace = /*#__PURE__*/_interopNamespace(ts);
|
|
64
65
|
var Handlebars__default = /*#__PURE__*/_interopDefault(Handlebars);
|
|
65
66
|
|
|
@@ -888,10 +889,27 @@ async function revokeCredentials({
|
|
|
888
889
|
});
|
|
889
890
|
emitAuthLogout(onEvent);
|
|
890
891
|
}
|
|
892
|
+
function getBaseUrlFromResolvedCredentials(credentials) {
|
|
893
|
+
if (credentials && zapierSdk.isCredentialsObject(credentials)) {
|
|
894
|
+
return credentials.baseUrl;
|
|
895
|
+
}
|
|
896
|
+
return void 0;
|
|
897
|
+
}
|
|
898
|
+
function getBaseUrlFromOptionsCredentials(credentials) {
|
|
899
|
+
if (credentials && typeof credentials === "object" && "baseUrl" in credentials && typeof credentials.baseUrl === "string") {
|
|
900
|
+
return credentials.baseUrl;
|
|
901
|
+
}
|
|
902
|
+
return void 0;
|
|
903
|
+
}
|
|
904
|
+
async function resolveCredentialsBaseUrl(context) {
|
|
905
|
+
const resolvedCredentials = "resolvedCredentials" in context ? context.resolvedCredentials : await context.resolveCredentials?.();
|
|
906
|
+
return getBaseUrlFromResolvedCredentials(resolvedCredentials) ?? getBaseUrlFromOptionsCredentials(context.options?.credentials) ?? context.options?.baseUrl;
|
|
907
|
+
}
|
|
891
908
|
|
|
892
|
-
// src/utils/
|
|
893
|
-
|
|
894
|
-
|
|
909
|
+
// src/utils/non-interactive.ts
|
|
910
|
+
function resolveNonInteractive(options) {
|
|
911
|
+
return options.nonInteractive === true || options.skipPrompts === true || !process.stdin.isTTY || !process.stdout.isTTY;
|
|
912
|
+
}
|
|
895
913
|
var ZapierCliError = class extends zapierSdk.ZapierError {
|
|
896
914
|
};
|
|
897
915
|
var ZapierCliUserCancellationError = class extends ZapierCliError {
|
|
@@ -919,22 +937,90 @@ var ZapierCliValidationError = class extends ZapierCliError {
|
|
|
919
937
|
}
|
|
920
938
|
};
|
|
921
939
|
|
|
922
|
-
// src/utils/
|
|
923
|
-
var
|
|
924
|
-
|
|
940
|
+
// src/utils/auth/client-credentials.ts
|
|
941
|
+
var CREDENTIALS_SCOPES = ["external", "credentials"];
|
|
942
|
+
var EMPTY_POLICY = {
|
|
943
|
+
version: 2,
|
|
944
|
+
statements: []
|
|
945
|
+
};
|
|
946
|
+
async function createCredentialsOnServer(api2, name, policy) {
|
|
947
|
+
const response = await api2.post(
|
|
948
|
+
"/api/v0/client-credentials",
|
|
949
|
+
{
|
|
950
|
+
name,
|
|
951
|
+
allowed_scopes: CREDENTIALS_SCOPES,
|
|
952
|
+
...policy !== void 0 && { policy }
|
|
953
|
+
},
|
|
954
|
+
{ authRequired: true, requiredScopes: ["credentials"] }
|
|
955
|
+
);
|
|
956
|
+
return {
|
|
957
|
+
clientId: response.data.client_id,
|
|
958
|
+
clientSecret: response.data.client_secret
|
|
959
|
+
};
|
|
960
|
+
}
|
|
961
|
+
async function deleteCredentialsOnServer(api2, clientId) {
|
|
962
|
+
await api2.delete(`/api/v0/client-credentials/${clientId}`, void 0, {
|
|
963
|
+
authRequired: true,
|
|
964
|
+
requiredScopes: ["credentials"]
|
|
965
|
+
});
|
|
966
|
+
}
|
|
967
|
+
async function setupClientCredentials({
|
|
968
|
+
api: api2,
|
|
969
|
+
name,
|
|
970
|
+
credentialsBaseUrl,
|
|
971
|
+
policy
|
|
972
|
+
}) {
|
|
973
|
+
const { clientId, clientSecret } = await createCredentialsOnServer(
|
|
974
|
+
api2,
|
|
975
|
+
name,
|
|
976
|
+
policy
|
|
977
|
+
);
|
|
925
978
|
try {
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
979
|
+
await withRetry({
|
|
980
|
+
action: () => storeClientCredentials({
|
|
981
|
+
name,
|
|
982
|
+
clientId,
|
|
983
|
+
clientSecret,
|
|
984
|
+
scopes: [...CREDENTIALS_SCOPES],
|
|
985
|
+
baseUrl: credentialsBaseUrl
|
|
986
|
+
})
|
|
987
|
+
});
|
|
988
|
+
} catch (storeErr) {
|
|
989
|
+
try {
|
|
990
|
+
await withRetry({
|
|
991
|
+
action: () => deleteCredentialsOnServer(api2, clientId)
|
|
992
|
+
});
|
|
993
|
+
} catch {
|
|
994
|
+
console.error(
|
|
995
|
+
`Failed to roll back orphaned credential ${clientId}. Delete it manually with: zapier-sdk delete-client-credentials ${clientId}`
|
|
996
|
+
);
|
|
934
997
|
}
|
|
935
|
-
throw
|
|
998
|
+
throw storeErr;
|
|
936
999
|
}
|
|
1000
|
+
return { clientId };
|
|
1001
|
+
}
|
|
1002
|
+
|
|
1003
|
+
// src/utils/constants.ts
|
|
1004
|
+
var LOGIN_PORTS = [49505, 50575, 52804, 55981, 61010, 63851];
|
|
1005
|
+
var LOGIN_TIMEOUT_MS = 3e5;
|
|
1006
|
+
|
|
1007
|
+
// src/utils/getCallablePromise.ts
|
|
1008
|
+
var getCallablePromise = () => {
|
|
1009
|
+
let resolve4 = () => {
|
|
1010
|
+
};
|
|
1011
|
+
let reject = () => {
|
|
1012
|
+
};
|
|
1013
|
+
const promise = new Promise((_resolve, _reject) => {
|
|
1014
|
+
resolve4 = _resolve;
|
|
1015
|
+
reject = _reject;
|
|
1016
|
+
});
|
|
1017
|
+
return {
|
|
1018
|
+
promise,
|
|
1019
|
+
resolve: resolve4,
|
|
1020
|
+
reject
|
|
1021
|
+
};
|
|
937
1022
|
};
|
|
1023
|
+
var getCallablePromise_default = getCallablePromise;
|
|
938
1024
|
var log = {
|
|
939
1025
|
info: (message, ...args) => {
|
|
940
1026
|
console.error(chalk3__default.default.blue("\u2139"), message, ...args);
|
|
@@ -955,6 +1041,60 @@ var log = {
|
|
|
955
1041
|
}
|
|
956
1042
|
};
|
|
957
1043
|
var log_default = log;
|
|
1044
|
+
var spinPromise = async (promise, text) => {
|
|
1045
|
+
const spinner = ora__default.default(text).start();
|
|
1046
|
+
try {
|
|
1047
|
+
const result = await promise;
|
|
1048
|
+
spinner.succeed();
|
|
1049
|
+
return result;
|
|
1050
|
+
} catch (error) {
|
|
1051
|
+
if (error instanceof ZapierCliUserCancellationError) {
|
|
1052
|
+
spinner.stop();
|
|
1053
|
+
} else {
|
|
1054
|
+
spinner.fail();
|
|
1055
|
+
}
|
|
1056
|
+
throw error;
|
|
1057
|
+
}
|
|
1058
|
+
};
|
|
1059
|
+
|
|
1060
|
+
// src/utils/auth/oauth-callback.ts
|
|
1061
|
+
function getCallbackCode({
|
|
1062
|
+
callbackUrl,
|
|
1063
|
+
transaction,
|
|
1064
|
+
recoveryMessage
|
|
1065
|
+
}) {
|
|
1066
|
+
let parsed;
|
|
1067
|
+
try {
|
|
1068
|
+
parsed = new URL(callbackUrl.trim());
|
|
1069
|
+
} catch {
|
|
1070
|
+
throw new ZapierCliValidationError(
|
|
1071
|
+
"Paste the final OAuth callback URL from your browser."
|
|
1072
|
+
);
|
|
1073
|
+
}
|
|
1074
|
+
const expected = new URL(transaction.redirectUri);
|
|
1075
|
+
if (parsed.protocol !== "http:" || parsed.hostname !== expected.hostname || parsed.pathname !== expected.pathname || parsed.port !== expected.port) {
|
|
1076
|
+
throw new ZapierCliValidationError(
|
|
1077
|
+
`Expected the final OAuth callback URL to start with ${transaction.redirectUri}.`
|
|
1078
|
+
);
|
|
1079
|
+
}
|
|
1080
|
+
if (parsed.searchParams.get("state") !== transaction.state) {
|
|
1081
|
+
throw new ZapierCliValidationError(
|
|
1082
|
+
`OAuth state mismatch.${recoveryMessage ? ` ${recoveryMessage}` : ""}`
|
|
1083
|
+
);
|
|
1084
|
+
}
|
|
1085
|
+
if (parsed.searchParams.has("error")) {
|
|
1086
|
+
throw new ZapierCliValidationError(
|
|
1087
|
+
`Authorization denied: ${parsed.searchParams.get("error_description") ?? parsed.searchParams.get("error")}.${recoveryMessage ? ` ${recoveryMessage}` : ""}`
|
|
1088
|
+
);
|
|
1089
|
+
}
|
|
1090
|
+
const code = parsed.searchParams.get("code");
|
|
1091
|
+
if (!code) {
|
|
1092
|
+
throw new ZapierCliValidationError(
|
|
1093
|
+
"No authorization code found in the pasted callback URL."
|
|
1094
|
+
);
|
|
1095
|
+
}
|
|
1096
|
+
return code;
|
|
1097
|
+
}
|
|
958
1098
|
|
|
959
1099
|
// src/utils/api/client.ts
|
|
960
1100
|
var createApiClient = () => {
|
|
@@ -985,179 +1125,77 @@ var createApiClient = () => {
|
|
|
985
1125
|
var api = createApiClient();
|
|
986
1126
|
var client_default = api;
|
|
987
1127
|
|
|
988
|
-
// src/utils/
|
|
989
|
-
var
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
}
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
// src/utils/auth/oauth-flow.ts
|
|
1007
|
-
var findAvailablePort = () => {
|
|
1008
|
-
return new Promise((resolve4, reject) => {
|
|
1009
|
-
let portIndex = 0;
|
|
1010
|
-
const tryPort = (port) => {
|
|
1011
|
-
const server = express__default.default().listen(port, () => {
|
|
1012
|
-
server.close();
|
|
1013
|
-
resolve4(port);
|
|
1014
|
-
});
|
|
1015
|
-
server.on("error", (err) => {
|
|
1016
|
-
if (err.code === "EADDRINUSE") {
|
|
1017
|
-
if (portIndex < LOGIN_PORTS.length) {
|
|
1018
|
-
tryPort(LOGIN_PORTS[portIndex++]);
|
|
1019
|
-
} else {
|
|
1020
|
-
reject(
|
|
1021
|
-
new Error(
|
|
1022
|
-
`All configured OAuth callback ports are busy: ${LOGIN_PORTS.join(", ")}. Please try again later or close applications using these ports.`
|
|
1023
|
-
)
|
|
1024
|
-
);
|
|
1025
|
-
}
|
|
1026
|
-
} else {
|
|
1027
|
-
reject(err);
|
|
1028
|
-
}
|
|
1029
|
-
});
|
|
1030
|
-
};
|
|
1031
|
-
if (LOGIN_PORTS.length > 0) {
|
|
1032
|
-
tryPort(LOGIN_PORTS[portIndex++]);
|
|
1033
|
-
} else {
|
|
1034
|
-
reject(new Error("No OAuth callback ports configured"));
|
|
1035
|
-
}
|
|
1036
|
-
});
|
|
1037
|
-
};
|
|
1038
|
-
var generateRandomString = () => {
|
|
1128
|
+
// src/utils/auth/oauth-transaction.ts
|
|
1129
|
+
var OAUTH_LOOPBACK_HOST = "localhost";
|
|
1130
|
+
function buildBrowserAuthUrl({
|
|
1131
|
+
authorizeUrl,
|
|
1132
|
+
entryPoint = "login"
|
|
1133
|
+
}) {
|
|
1134
|
+
if (entryPoint === "login") return authorizeUrl;
|
|
1135
|
+
const parsedAuthorizeUrl = new URL(authorizeUrl);
|
|
1136
|
+
const signupUrl = new URL("/sign-up", parsedAuthorizeUrl);
|
|
1137
|
+
signupUrl.searchParams.set("skipOnboarding", "true");
|
|
1138
|
+
signupUrl.searchParams.set(
|
|
1139
|
+
"next",
|
|
1140
|
+
`${parsedAuthorizeUrl.pathname}${parsedAuthorizeUrl.search}`
|
|
1141
|
+
);
|
|
1142
|
+
return signupUrl.toString();
|
|
1143
|
+
}
|
|
1144
|
+
function generateRandomString() {
|
|
1039
1145
|
const array = new Uint32Array(28);
|
|
1040
1146
|
crypto__default.default.getRandomValues(array);
|
|
1041
1147
|
return Array.from(array, (dec) => ("0" + dec.toString(16)).slice(-2)).join(
|
|
1042
1148
|
""
|
|
1043
1149
|
);
|
|
1044
|
-
}
|
|
1150
|
+
}
|
|
1045
1151
|
function ensureOfflineAccess(scope) {
|
|
1046
|
-
if (scope.includes("offline_access"))
|
|
1047
|
-
return scope;
|
|
1048
|
-
}
|
|
1152
|
+
if (scope.includes("offline_access")) return scope;
|
|
1049
1153
|
return `${scope} offline_access`;
|
|
1050
1154
|
}
|
|
1051
|
-
async function
|
|
1052
|
-
timeoutMs = LOGIN_TIMEOUT_MS,
|
|
1155
|
+
async function prepareOauthTransaction({
|
|
1053
1156
|
pkceCredentials,
|
|
1054
|
-
baseUrl
|
|
1157
|
+
baseUrl,
|
|
1158
|
+
redirectUri,
|
|
1159
|
+
entryPoint = "login"
|
|
1055
1160
|
}) {
|
|
1056
1161
|
const { clientId, tokenUrl, authorizeUrl } = getPkceLoginConfig({
|
|
1057
1162
|
credentials: pkceCredentials,
|
|
1058
1163
|
baseUrl
|
|
1059
1164
|
});
|
|
1060
|
-
const scope = ensureOfflineAccess(
|
|
1061
|
-
pkceCredentials?.scope || "internal credentials"
|
|
1062
|
-
);
|
|
1063
|
-
const availablePort = await findAvailablePort();
|
|
1064
|
-
const redirectUri = `http://localhost:${availablePort}/oauth`;
|
|
1065
|
-
log_default.info(`Using port ${availablePort} for OAuth callback`);
|
|
1066
|
-
const {
|
|
1067
|
-
promise: promisedCode,
|
|
1068
|
-
resolve: setCode,
|
|
1069
|
-
reject: rejectCode
|
|
1070
|
-
} = getCallablePromise_default();
|
|
1071
|
-
const oauthState = generateRandomString();
|
|
1072
|
-
const expressApp = express__default.default();
|
|
1073
|
-
expressApp.get("/oauth", (req, res) => {
|
|
1074
|
-
res.setHeader("Connection", "close");
|
|
1075
|
-
if (req.query.state !== oauthState) {
|
|
1076
|
-
rejectCode(new Error("OAuth state mismatch \u2014 possible CSRF"));
|
|
1077
|
-
res.status(400).end("Invalid state. You can close this tab.");
|
|
1078
|
-
return;
|
|
1079
|
-
}
|
|
1080
|
-
if (req.query.error) {
|
|
1081
|
-
const desc = req.query.error_description ?? req.query.error;
|
|
1082
|
-
rejectCode(new Error(`Authorization denied: ${desc}`));
|
|
1083
|
-
res.end("Authorization was denied. You can close this tab.");
|
|
1084
|
-
return;
|
|
1085
|
-
}
|
|
1086
|
-
if (!req.query.code) {
|
|
1087
|
-
rejectCode(new Error("No authorization code received"));
|
|
1088
|
-
res.end("No authorization code received. You can close this tab.");
|
|
1089
|
-
return;
|
|
1090
|
-
}
|
|
1091
|
-
setCode(String(req.query.code));
|
|
1092
|
-
res.end("You can now close this tab and return to the CLI.");
|
|
1093
|
-
});
|
|
1094
|
-
const server = expressApp.listen(availablePort);
|
|
1095
|
-
const connections = /* @__PURE__ */ new Set();
|
|
1096
|
-
server.on("connection", (conn) => {
|
|
1097
|
-
connections.add(conn);
|
|
1098
|
-
conn.on("close", () => connections.delete(conn));
|
|
1099
|
-
});
|
|
1100
|
-
const cleanup = () => {
|
|
1101
|
-
server.close();
|
|
1102
|
-
log_default.info("\n\u274C Login cancelled by user");
|
|
1103
|
-
rejectCode(new ZapierCliUserCancellationError());
|
|
1104
|
-
};
|
|
1105
|
-
process.on("SIGINT", cleanup);
|
|
1106
|
-
process.on("SIGTERM", cleanup);
|
|
1107
1165
|
const { code_verifier: codeVerifier, code_challenge: codeChallenge } = await pkceChallenge__default.default();
|
|
1166
|
+
const state = generateRandomString();
|
|
1108
1167
|
const authUrl = `${authorizeUrl}?${new URLSearchParams({
|
|
1109
1168
|
response_type: "code",
|
|
1110
1169
|
client_id: clientId,
|
|
1111
1170
|
redirect_uri: redirectUri,
|
|
1112
|
-
scope
|
|
1113
|
-
|
|
1171
|
+
scope: ensureOfflineAccess(
|
|
1172
|
+
pkceCredentials?.scope || "internal credentials"
|
|
1173
|
+
),
|
|
1174
|
+
state,
|
|
1114
1175
|
code_challenge: codeChallenge,
|
|
1115
1176
|
code_challenge_method: "S256"
|
|
1116
1177
|
}).toString()}`;
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
})
|
|
1134
|
-
]),
|
|
1135
|
-
"Waiting for you to login and authorize"
|
|
1136
|
-
);
|
|
1137
|
-
} finally {
|
|
1138
|
-
if (timeoutTimer) {
|
|
1139
|
-
clearTimeout(timeoutTimer);
|
|
1140
|
-
}
|
|
1141
|
-
process.off("SIGINT", cleanup);
|
|
1142
|
-
process.off("SIGTERM", cleanup);
|
|
1143
|
-
await new Promise((resolve4) => {
|
|
1144
|
-
const timeout = setTimeout(() => {
|
|
1145
|
-
log_default.info("Server close timed out, forcing connection shutdown...");
|
|
1146
|
-
connections.forEach((conn) => conn.destroy());
|
|
1147
|
-
resolve4();
|
|
1148
|
-
}, 1e3);
|
|
1149
|
-
server.close(() => {
|
|
1150
|
-
clearTimeout(timeout);
|
|
1151
|
-
resolve4();
|
|
1152
|
-
});
|
|
1153
|
-
});
|
|
1154
|
-
}
|
|
1155
|
-
log_default.info("Exchanging authorization code for tokens...");
|
|
1178
|
+
return {
|
|
1179
|
+
browserAuthUrl: buildBrowserAuthUrl({ authorizeUrl: authUrl, entryPoint }),
|
|
1180
|
+
clientId,
|
|
1181
|
+
codeVerifier,
|
|
1182
|
+
redirectUri,
|
|
1183
|
+
state,
|
|
1184
|
+
tokenUrl
|
|
1185
|
+
};
|
|
1186
|
+
}
|
|
1187
|
+
async function exchangeOauthCode({
|
|
1188
|
+
tokenUrl,
|
|
1189
|
+
code,
|
|
1190
|
+
redirectUri,
|
|
1191
|
+
clientId,
|
|
1192
|
+
codeVerifier
|
|
1193
|
+
}) {
|
|
1156
1194
|
const { data } = await client_default.post(
|
|
1157
1195
|
tokenUrl,
|
|
1158
1196
|
{
|
|
1159
1197
|
grant_type: "authorization_code",
|
|
1160
|
-
code
|
|
1198
|
+
code,
|
|
1161
1199
|
redirect_uri: redirectUri,
|
|
1162
1200
|
client_id: clientId,
|
|
1163
1201
|
code_verifier: codeVerifier
|
|
@@ -1169,7 +1207,6 @@ async function runOauthFlow({
|
|
|
1169
1207
|
}
|
|
1170
1208
|
}
|
|
1171
1209
|
);
|
|
1172
|
-
log_default.info("Token exchange completed successfully");
|
|
1173
1210
|
return {
|
|
1174
1211
|
accessToken: data.access_token,
|
|
1175
1212
|
refreshToken: data.refresh_token,
|
|
@@ -1177,105 +1214,467 @@ async function runOauthFlow({
|
|
|
1177
1214
|
};
|
|
1178
1215
|
}
|
|
1179
1216
|
|
|
1180
|
-
// src/utils/auth/
|
|
1181
|
-
var
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1217
|
+
// src/utils/auth/oauth-flow.ts
|
|
1218
|
+
var OauthFlowTimeoutError = class extends Error {
|
|
1219
|
+
constructor(timeoutMs) {
|
|
1220
|
+
super("OAuth flow timed out");
|
|
1221
|
+
this.timeoutMs = timeoutMs;
|
|
1222
|
+
this.name = "OauthFlowTimeoutError";
|
|
1223
|
+
}
|
|
1185
1224
|
};
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
"
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1225
|
+
var OauthAuthorizationDeniedError = class extends Error {
|
|
1226
|
+
constructor(reason) {
|
|
1227
|
+
super("OAuth authorization denied");
|
|
1228
|
+
this.reason = reason;
|
|
1229
|
+
this.name = "OauthAuthorizationDeniedError";
|
|
1230
|
+
}
|
|
1231
|
+
};
|
|
1232
|
+
function findAvailablePort() {
|
|
1233
|
+
return new Promise((resolve4, reject) => {
|
|
1234
|
+
let portIndex = 0;
|
|
1235
|
+
const tryPort = (port) => {
|
|
1236
|
+
const server = express__default.default().listen(port, OAUTH_LOOPBACK_HOST, () => {
|
|
1237
|
+
server.close();
|
|
1238
|
+
resolve4(port);
|
|
1239
|
+
});
|
|
1240
|
+
server.on("error", (err) => {
|
|
1241
|
+
if (err.code === "EADDRINUSE" && portIndex < LOGIN_PORTS.length) {
|
|
1242
|
+
tryPort(LOGIN_PORTS[portIndex++]);
|
|
1243
|
+
} else if (err.code === "EADDRINUSE") {
|
|
1244
|
+
reject(
|
|
1245
|
+
new Error(
|
|
1246
|
+
`All configured OAuth callback ports are busy: ${LOGIN_PORTS.join(", ")}. Please try again later or close applications using these ports.`
|
|
1247
|
+
)
|
|
1248
|
+
);
|
|
1249
|
+
} else {
|
|
1250
|
+
reject(err);
|
|
1251
|
+
}
|
|
1252
|
+
});
|
|
1253
|
+
};
|
|
1254
|
+
if (LOGIN_PORTS.length > 0) tryPort(LOGIN_PORTS[portIndex++]);
|
|
1255
|
+
else reject(new Error("No OAuth callback ports configured"));
|
|
1256
|
+
});
|
|
1200
1257
|
}
|
|
1201
|
-
async function
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1258
|
+
async function runLoginOauthFlow(options) {
|
|
1259
|
+
return runOauthFlowEntryPoint({
|
|
1260
|
+
...options,
|
|
1261
|
+
entryPoint: "login",
|
|
1262
|
+
authAction: "log in",
|
|
1263
|
+
flowName: "Login"
|
|
1205
1264
|
});
|
|
1206
1265
|
}
|
|
1207
|
-
async function
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1266
|
+
async function runSignupOauthFlow(options) {
|
|
1267
|
+
if (options.headless) {
|
|
1268
|
+
return runOauthFlowEntryPoint({
|
|
1269
|
+
...options,
|
|
1270
|
+
entryPoint: "signup",
|
|
1271
|
+
authAction: "sign up",
|
|
1272
|
+
flowName: "Signup",
|
|
1273
|
+
headless: true
|
|
1274
|
+
});
|
|
1275
|
+
}
|
|
1276
|
+
return runOauthFlowEntryPoint({
|
|
1277
|
+
...options,
|
|
1278
|
+
entryPoint: "signup",
|
|
1279
|
+
authAction: "sign up",
|
|
1280
|
+
flowName: "Signup"
|
|
1281
|
+
});
|
|
1282
|
+
}
|
|
1283
|
+
async function runOauthFlowEntryPoint({
|
|
1284
|
+
flowName,
|
|
1285
|
+
...options
|
|
1212
1286
|
}) {
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1287
|
+
try {
|
|
1288
|
+
return options.headless ? await runHeadlessSignupOauthFlow(options) : await runOauthFlow(options);
|
|
1289
|
+
} catch (error) {
|
|
1290
|
+
if (error instanceof OauthFlowTimeoutError) {
|
|
1291
|
+
throw new Error(
|
|
1292
|
+
withRecoveryMessage(
|
|
1293
|
+
`${flowName} timed out after ${Math.round(error.timeoutMs / 1e3)} seconds.`,
|
|
1294
|
+
options.recoveryMessage
|
|
1295
|
+
)
|
|
1296
|
+
);
|
|
1297
|
+
}
|
|
1298
|
+
if (error instanceof OauthAuthorizationDeniedError) {
|
|
1299
|
+
throw new Error(
|
|
1300
|
+
withRecoveryMessage(
|
|
1301
|
+
`Authorization denied: ${error.reason}.`,
|
|
1302
|
+
options.recoveryMessage
|
|
1303
|
+
)
|
|
1304
|
+
);
|
|
1305
|
+
}
|
|
1306
|
+
if (error instanceof ZapierCliUserCancellationError && !options.silent) {
|
|
1307
|
+
log_default.info(`
|
|
1308
|
+
\u274C ${flowName} cancelled by user`);
|
|
1309
|
+
}
|
|
1310
|
+
throw error;
|
|
1311
|
+
}
|
|
1312
|
+
}
|
|
1313
|
+
function withRecoveryMessage(message, recoveryMessage) {
|
|
1314
|
+
return recoveryMessage ? `${message} ${recoveryMessage}` : message;
|
|
1315
|
+
}
|
|
1316
|
+
async function runOauthFlow({
|
|
1317
|
+
timeoutMs = LOGIN_TIMEOUT_MS,
|
|
1318
|
+
pkceCredentials,
|
|
1319
|
+
baseUrl,
|
|
1320
|
+
entryPoint,
|
|
1321
|
+
authAction,
|
|
1322
|
+
silent = false,
|
|
1323
|
+
onProgress
|
|
1324
|
+
}) {
|
|
1325
|
+
const port = await findAvailablePort();
|
|
1326
|
+
if (!silent) log_default.info(`Using port ${port} for OAuth callback`);
|
|
1327
|
+
const transaction = await prepareOauthTransaction({
|
|
1328
|
+
pkceCredentials,
|
|
1329
|
+
baseUrl,
|
|
1330
|
+
redirectUri: `http://${OAUTH_LOOPBACK_HOST}:${port}/oauth`,
|
|
1331
|
+
entryPoint
|
|
1332
|
+
});
|
|
1333
|
+
const code = await collectLocalCallbackCode({
|
|
1334
|
+
transaction,
|
|
1335
|
+
timeoutMs,
|
|
1336
|
+
authAction,
|
|
1337
|
+
silent,
|
|
1338
|
+
onProgress
|
|
1339
|
+
});
|
|
1340
|
+
onProgress?.({ type: "callback_accepted" });
|
|
1341
|
+
if (!silent) log_default.info("Exchanging authorization code for tokens...");
|
|
1342
|
+
onProgress?.({ type: "token_exchange_started" });
|
|
1343
|
+
const tokens = await exchangeOauthCode({ ...transaction, code });
|
|
1344
|
+
if (!silent) log_default.info("Token exchange completed successfully");
|
|
1345
|
+
onProgress?.({ type: "token_exchange_completed" });
|
|
1346
|
+
return tokens;
|
|
1347
|
+
}
|
|
1348
|
+
async function readHeadlessCallbackUrl({
|
|
1349
|
+
timeoutMs,
|
|
1350
|
+
interactive,
|
|
1351
|
+
recoveryMessage
|
|
1352
|
+
}) {
|
|
1353
|
+
const timeoutMessage = withRecoveryMessage(
|
|
1354
|
+
`Signup timed out after ${Math.round(timeoutMs / 1e3)} seconds.`,
|
|
1355
|
+
recoveryMessage
|
|
1356
|
+
);
|
|
1357
|
+
const missingCallbackUrlMessage = withRecoveryMessage(
|
|
1358
|
+
"Paste the final OAuth callback URL from your browser.",
|
|
1359
|
+
recoveryMessage
|
|
1217
1360
|
);
|
|
1361
|
+
const rl = promises$1.createInterface({ input: process.stdin, output: process.stderr });
|
|
1362
|
+
const abortController = new AbortController();
|
|
1363
|
+
const timeoutTimer = setTimeout(() => abortController.abort(), timeoutMs);
|
|
1364
|
+
const readUrl = interactive ? rl.question("Paste the final OAuth callback URL: ", {
|
|
1365
|
+
signal: abortController.signal
|
|
1366
|
+
}) : new Promise((resolve4, reject) => {
|
|
1367
|
+
let settled = false;
|
|
1368
|
+
const settleResolve = (value) => {
|
|
1369
|
+
settled = true;
|
|
1370
|
+
resolve4(value);
|
|
1371
|
+
};
|
|
1372
|
+
const settleReject = (error) => {
|
|
1373
|
+
if (settled) return;
|
|
1374
|
+
settled = true;
|
|
1375
|
+
reject(error);
|
|
1376
|
+
};
|
|
1377
|
+
abortController.signal.addEventListener(
|
|
1378
|
+
"abort",
|
|
1379
|
+
() => settleReject(new Error(timeoutMessage)),
|
|
1380
|
+
{ once: true }
|
|
1381
|
+
);
|
|
1382
|
+
rl.once("line", settleResolve);
|
|
1383
|
+
rl.once(
|
|
1384
|
+
"close",
|
|
1385
|
+
() => settleReject(new ZapierCliValidationError(missingCallbackUrlMessage))
|
|
1386
|
+
);
|
|
1387
|
+
rl.once("error", settleReject);
|
|
1388
|
+
});
|
|
1218
1389
|
try {
|
|
1219
|
-
await
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
scopes: [...CREDENTIALS_SCOPES],
|
|
1225
|
-
baseUrl: credentialsBaseUrl
|
|
1226
|
-
})
|
|
1390
|
+
return await readUrl.catch((error) => {
|
|
1391
|
+
if (error instanceof Error && error.name === "AbortError") {
|
|
1392
|
+
throw new Error(timeoutMessage);
|
|
1393
|
+
}
|
|
1394
|
+
throw error;
|
|
1227
1395
|
});
|
|
1228
|
-
}
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1396
|
+
} finally {
|
|
1397
|
+
clearTimeout(timeoutTimer);
|
|
1398
|
+
rl.close();
|
|
1399
|
+
}
|
|
1400
|
+
}
|
|
1401
|
+
async function runHeadlessSignupOauthFlow({
|
|
1402
|
+
timeoutMs = LOGIN_TIMEOUT_MS,
|
|
1403
|
+
pkceCredentials,
|
|
1404
|
+
baseUrl,
|
|
1405
|
+
interactive = true,
|
|
1406
|
+
onProgress,
|
|
1407
|
+
recoveryMessage
|
|
1408
|
+
}) {
|
|
1409
|
+
const port = LOGIN_PORTS[0];
|
|
1410
|
+
const transaction = await prepareOauthTransaction({
|
|
1411
|
+
pkceCredentials,
|
|
1412
|
+
baseUrl,
|
|
1413
|
+
redirectUri: `http://${OAUTH_LOOPBACK_HOST}:${port}/oauth`,
|
|
1414
|
+
entryPoint: "signup"
|
|
1415
|
+
});
|
|
1416
|
+
console.log(
|
|
1417
|
+
"Use this mode when signing up from a machine that has no browser."
|
|
1418
|
+
);
|
|
1419
|
+
console.log("Open this signup URL in a browser on another machine:");
|
|
1420
|
+
console.log(transaction.browserAuthUrl);
|
|
1421
|
+
console.log(
|
|
1422
|
+
`When the browser lands on ${transaction.redirectUri} and cannot connect, paste the full final URL back here.`
|
|
1423
|
+
);
|
|
1424
|
+
const callbackUrl = await readHeadlessCallbackUrl({
|
|
1425
|
+
timeoutMs,
|
|
1426
|
+
interactive,
|
|
1427
|
+
recoveryMessage
|
|
1428
|
+
});
|
|
1429
|
+
const code = getCallbackCode({
|
|
1430
|
+
callbackUrl,
|
|
1431
|
+
transaction,
|
|
1432
|
+
recoveryMessage
|
|
1433
|
+
});
|
|
1434
|
+
onProgress?.({ type: "callback_accepted" });
|
|
1435
|
+
console.log("Exchanging authorization code for tokens...");
|
|
1436
|
+
onProgress?.({ type: "token_exchange_started" });
|
|
1437
|
+
const tokens = await exchangeOauthCode({ ...transaction, code });
|
|
1438
|
+
onProgress?.({ type: "token_exchange_completed" });
|
|
1439
|
+
return tokens;
|
|
1440
|
+
}
|
|
1441
|
+
async function collectLocalCallbackCode({
|
|
1442
|
+
transaction,
|
|
1443
|
+
timeoutMs,
|
|
1444
|
+
authAction,
|
|
1445
|
+
silent,
|
|
1446
|
+
onProgress
|
|
1447
|
+
}) {
|
|
1448
|
+
const { promise, resolve: resolve4, reject } = getCallablePromise_default();
|
|
1449
|
+
const app = express__default.default();
|
|
1450
|
+
app.get("/oauth", (req, res) => {
|
|
1451
|
+
res.setHeader("Connection", "close");
|
|
1452
|
+
if (req.query.state !== transaction.state) {
|
|
1453
|
+
res.status(400).end("Invalid state. You can close this tab.");
|
|
1454
|
+
} else if (req.query.error) {
|
|
1455
|
+
reject(
|
|
1456
|
+
new OauthAuthorizationDeniedError(
|
|
1457
|
+
String(req.query.error_description ?? req.query.error)
|
|
1458
|
+
)
|
|
1236
1459
|
);
|
|
1460
|
+
res.end("Authorization was denied. You can close this tab.");
|
|
1461
|
+
} else if (!req.query.code) {
|
|
1462
|
+
reject(new Error("No authorization code received"));
|
|
1463
|
+
res.end("No authorization code received. You can close this tab.");
|
|
1464
|
+
} else {
|
|
1465
|
+
resolve4(String(req.query.code));
|
|
1466
|
+
res.end("You can now close this tab and return to the CLI.");
|
|
1237
1467
|
}
|
|
1238
|
-
|
|
1468
|
+
});
|
|
1469
|
+
const server = app.listen(
|
|
1470
|
+
Number(new URL(transaction.redirectUri).port),
|
|
1471
|
+
OAUTH_LOOPBACK_HOST
|
|
1472
|
+
);
|
|
1473
|
+
const connections = /* @__PURE__ */ new Set();
|
|
1474
|
+
server.on("connection", (conn) => {
|
|
1475
|
+
connections.add(conn);
|
|
1476
|
+
conn.on("close", () => connections.delete(conn));
|
|
1477
|
+
});
|
|
1478
|
+
const cleanup = () => {
|
|
1479
|
+
server.close();
|
|
1480
|
+
reject(new ZapierCliUserCancellationError());
|
|
1481
|
+
};
|
|
1482
|
+
process.on("SIGINT", cleanup);
|
|
1483
|
+
process.on("SIGTERM", cleanup);
|
|
1484
|
+
let timeoutTimer;
|
|
1485
|
+
try {
|
|
1486
|
+
await waitForServerListening(server);
|
|
1487
|
+
await openBrowser({ transaction, authAction, silent, onProgress });
|
|
1488
|
+
const waitForCode = Promise.race([
|
|
1489
|
+
promise,
|
|
1490
|
+
new Promise((_resolve, rejectTimeout) => {
|
|
1491
|
+
timeoutTimer = setTimeout(() => {
|
|
1492
|
+
rejectTimeout(new OauthFlowTimeoutError(timeoutMs));
|
|
1493
|
+
}, timeoutMs);
|
|
1494
|
+
})
|
|
1495
|
+
]);
|
|
1496
|
+
onProgress?.({ type: "callback_waiting" });
|
|
1497
|
+
return silent ? await waitForCode : await spinPromise(
|
|
1498
|
+
waitForCode,
|
|
1499
|
+
`Waiting for you to ${authAction} and authorize`
|
|
1500
|
+
);
|
|
1501
|
+
} finally {
|
|
1502
|
+
if (timeoutTimer) clearTimeout(timeoutTimer);
|
|
1503
|
+
process.off("SIGINT", cleanup);
|
|
1504
|
+
process.off("SIGTERM", cleanup);
|
|
1505
|
+
await closeServer({ server, connections, silent });
|
|
1239
1506
|
}
|
|
1240
|
-
return { clientId };
|
|
1241
1507
|
}
|
|
1242
|
-
function
|
|
1243
|
-
if (
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1508
|
+
async function waitForServerListening(server) {
|
|
1509
|
+
if (server.listening) return;
|
|
1510
|
+
await new Promise((resolve4, reject) => {
|
|
1511
|
+
const cleanup = () => {
|
|
1512
|
+
server.off("listening", handleListening);
|
|
1513
|
+
server.off("error", handleError);
|
|
1514
|
+
};
|
|
1515
|
+
const handleListening = () => {
|
|
1516
|
+
cleanup();
|
|
1517
|
+
resolve4();
|
|
1518
|
+
};
|
|
1519
|
+
const handleError = (error) => {
|
|
1520
|
+
cleanup();
|
|
1521
|
+
reject(error);
|
|
1522
|
+
};
|
|
1523
|
+
server.once("listening", handleListening);
|
|
1524
|
+
server.once("error", handleError);
|
|
1525
|
+
});
|
|
1247
1526
|
}
|
|
1248
|
-
function
|
|
1249
|
-
|
|
1250
|
-
|
|
1527
|
+
async function openBrowser({
|
|
1528
|
+
transaction,
|
|
1529
|
+
authAction,
|
|
1530
|
+
silent,
|
|
1531
|
+
onProgress
|
|
1532
|
+
}) {
|
|
1533
|
+
if (!silent) {
|
|
1534
|
+
log_default.info(`Opening your browser to ${authAction}.`);
|
|
1535
|
+
log_default.info("If it doesn't open, visit:", transaction.browserAuthUrl);
|
|
1536
|
+
}
|
|
1537
|
+
onProgress?.({ type: "browser_opening", url: transaction.browserAuthUrl });
|
|
1538
|
+
try {
|
|
1539
|
+
await open__default.default(transaction.browserAuthUrl);
|
|
1540
|
+
onProgress?.({ type: "browser_opened", url: transaction.browserAuthUrl });
|
|
1541
|
+
} catch (err) {
|
|
1542
|
+
const reason = err instanceof Error ? err.message : String(err);
|
|
1543
|
+
if (!silent) {
|
|
1544
|
+
log_default.info(
|
|
1545
|
+
`Browser did not open automatically to ${authAction}: ${reason}`
|
|
1546
|
+
);
|
|
1547
|
+
log_default.info("Visit this URL manually:", transaction.browserAuthUrl);
|
|
1548
|
+
}
|
|
1549
|
+
onProgress?.({
|
|
1550
|
+
type: "browser_open_failed",
|
|
1551
|
+
url: transaction.browserAuthUrl,
|
|
1552
|
+
reason
|
|
1553
|
+
});
|
|
1251
1554
|
}
|
|
1252
|
-
return void 0;
|
|
1253
1555
|
}
|
|
1254
|
-
async function
|
|
1255
|
-
|
|
1256
|
-
|
|
1556
|
+
async function closeServer({
|
|
1557
|
+
server,
|
|
1558
|
+
connections,
|
|
1559
|
+
silent
|
|
1560
|
+
}) {
|
|
1561
|
+
await new Promise((resolve4) => {
|
|
1562
|
+
const timeout = setTimeout(() => {
|
|
1563
|
+
if (!silent)
|
|
1564
|
+
log_default.info("Server close timed out, forcing connection shutdown...");
|
|
1565
|
+
connections.forEach((conn) => conn.destroy());
|
|
1566
|
+
resolve4();
|
|
1567
|
+
}, 1e3);
|
|
1568
|
+
server.close(() => {
|
|
1569
|
+
clearTimeout(timeout);
|
|
1570
|
+
resolve4();
|
|
1571
|
+
});
|
|
1572
|
+
});
|
|
1257
1573
|
}
|
|
1258
1574
|
|
|
1259
|
-
// src/utils/
|
|
1260
|
-
|
|
1261
|
-
|
|
1575
|
+
// src/utils/auth/oauth-errors.ts
|
|
1576
|
+
var SENSITIVE_OAUTH_FIELDS = [
|
|
1577
|
+
"access_token",
|
|
1578
|
+
"refresh_token",
|
|
1579
|
+
"id_token",
|
|
1580
|
+
"client_secret",
|
|
1581
|
+
"code_verifier",
|
|
1582
|
+
"code_challenge"
|
|
1583
|
+
];
|
|
1584
|
+
function getErrorMessage(error) {
|
|
1585
|
+
return error instanceof Error ? error.message : String(error);
|
|
1586
|
+
}
|
|
1587
|
+
function toCamelCase(field) {
|
|
1588
|
+
return field.replace(
|
|
1589
|
+
/_([a-z])/g,
|
|
1590
|
+
(_match, letter) => letter.toUpperCase()
|
|
1591
|
+
);
|
|
1592
|
+
}
|
|
1593
|
+
function escapeRegExp(value) {
|
|
1594
|
+
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
1595
|
+
}
|
|
1596
|
+
var sensitiveOauthFieldPattern = Array.from(
|
|
1597
|
+
new Set(
|
|
1598
|
+
SENSITIVE_OAUTH_FIELDS.flatMap((field) => [field, toCamelCase(field)])
|
|
1599
|
+
)
|
|
1600
|
+
).map(escapeRegExp).join("|");
|
|
1601
|
+
var sensitiveQueryParamPattern = new RegExp(
|
|
1602
|
+
`([?&])(${sensitiveOauthFieldPattern})(=)[^&#\\s"'<>]*`,
|
|
1603
|
+
"gi"
|
|
1604
|
+
);
|
|
1605
|
+
function redactSensitiveOauthErrorMessage(message) {
|
|
1606
|
+
return message.replace(
|
|
1607
|
+
sensitiveQueryParamPattern,
|
|
1608
|
+
(_match, prefix, key, separator) => `${prefix}${key}${separator}[REDACTED]`
|
|
1609
|
+
).replace(
|
|
1610
|
+
new RegExp(`"(${sensitiveOauthFieldPattern})"(\\s*:\\s*)"[^"]*"`, "g"),
|
|
1611
|
+
(_match, key, separator) => `"${key}"${separator}"[REDACTED]"`
|
|
1612
|
+
);
|
|
1613
|
+
}
|
|
1614
|
+
function toRedactedOauthError(error) {
|
|
1615
|
+
const message = redactSensitiveOauthErrorMessage(getErrorMessage(error));
|
|
1616
|
+
if (error instanceof ZapierCliValidationError) {
|
|
1617
|
+
return new ZapierCliValidationError(message);
|
|
1618
|
+
}
|
|
1619
|
+
if (error instanceof Error) {
|
|
1620
|
+
const redactedError = new Error(message);
|
|
1621
|
+
redactedError.name = error.name;
|
|
1622
|
+
return redactedError;
|
|
1623
|
+
}
|
|
1624
|
+
return new ZapierCliValidationError(message);
|
|
1262
1625
|
}
|
|
1263
|
-
var LoginSchema = zod.z.object({
|
|
1264
|
-
timeout: zod.z.string().optional().describe("Login timeout in seconds (default: 300)"),
|
|
1265
|
-
useApprovals: zod.z.boolean().optional().describe(
|
|
1266
|
-
"Require approvals for actions performed with these credentials"
|
|
1267
|
-
),
|
|
1268
|
-
nonInteractive: zod.z.boolean().optional().describe(
|
|
1269
|
-
"Skip interactive prompts. Uses defaults where possible; errors instead of prompting when input is required. Useful in CI, piped output, or environments where TTY detection is unreliable."
|
|
1270
|
-
),
|
|
1271
|
-
/** @deprecated Use `nonInteractive` instead. */
|
|
1272
|
-
skipPrompts: zod.z.boolean().optional().meta({
|
|
1273
|
-
deprecated: true,
|
|
1274
|
-
deprecationMessage: "Use --non-interactive instead."
|
|
1275
|
-
})
|
|
1276
|
-
}).describe("Log in to Zapier to access your account");
|
|
1277
1626
|
|
|
1278
|
-
// src/
|
|
1627
|
+
// src/utils/auth/account-auth.ts
|
|
1628
|
+
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?";
|
|
1629
|
+
var SIGNUP_RECOVERY_MESSAGE = "Restart `zapier-sdk signup` to generate a fresh signup URL and try again.";
|
|
1630
|
+
var HEADLESS_SIGNUP_RECOVERY_MESSAGE = "Restart `zapier-sdk signup --headless` to generate a fresh signup URL and try again.";
|
|
1631
|
+
function getEntryPointLabel(entryPoint) {
|
|
1632
|
+
return entryPoint === "signup" ? "Signup" : "Login";
|
|
1633
|
+
}
|
|
1634
|
+
function getActiveCredentialsAction(entryPoint) {
|
|
1635
|
+
return entryPoint === "signup" ? "continue signup" : "log in again";
|
|
1636
|
+
}
|
|
1637
|
+
function getCredentialsPromptMessage(entryPoint) {
|
|
1638
|
+
return entryPoint === "signup" ? "Enter a name to identify these credentials:" : "Enter a name to identify them:";
|
|
1639
|
+
}
|
|
1640
|
+
function getProfileMessage(entryPoint, email) {
|
|
1641
|
+
return entryPoint === "signup" ? `\u{1F464} Authenticated as ${email}` : `\u{1F464} Logged in as ${email}`;
|
|
1642
|
+
}
|
|
1643
|
+
function defaultCredentialsName(email) {
|
|
1644
|
+
return `${email}@${os.hostname()}`;
|
|
1645
|
+
}
|
|
1646
|
+
function validateCredentialsName(name) {
|
|
1647
|
+
const trimmedName = name.trim();
|
|
1648
|
+
if (!trimmedName) throw new ZapierCliValidationError("Name cannot be empty");
|
|
1649
|
+
return trimmedName;
|
|
1650
|
+
}
|
|
1651
|
+
async function promptCredentialsName({
|
|
1652
|
+
email,
|
|
1653
|
+
promptMessage
|
|
1654
|
+
}) {
|
|
1655
|
+
const { credentialName } = await inquirer__default.default.prompt([
|
|
1656
|
+
{
|
|
1657
|
+
type: "input",
|
|
1658
|
+
name: "credentialName",
|
|
1659
|
+
message: promptMessage,
|
|
1660
|
+
default: defaultCredentialsName(email),
|
|
1661
|
+
validate: (input) => {
|
|
1662
|
+
try {
|
|
1663
|
+
validateCredentialsName(input);
|
|
1664
|
+
return true;
|
|
1665
|
+
} catch (err) {
|
|
1666
|
+
return err instanceof Error ? err.message : String(err);
|
|
1667
|
+
}
|
|
1668
|
+
}
|
|
1669
|
+
}
|
|
1670
|
+
]);
|
|
1671
|
+
return validateCredentialsName(credentialName);
|
|
1672
|
+
}
|
|
1673
|
+
function resolveDefaultCredentialsName({
|
|
1674
|
+
email
|
|
1675
|
+
}) {
|
|
1676
|
+
return validateCredentialsName(defaultCredentialsName(email));
|
|
1677
|
+
}
|
|
1279
1678
|
function toPkceCredentials(credentials) {
|
|
1280
1679
|
if (credentials && zapierSdk.isCredentialsObject(credentials) && !("clientSecret" in credentials)) {
|
|
1281
1680
|
return {
|
|
@@ -1287,104 +1686,125 @@ function toPkceCredentials(credentials) {
|
|
|
1287
1686
|
}
|
|
1288
1687
|
return void 0;
|
|
1289
1688
|
}
|
|
1290
|
-
|
|
1291
|
-
if (
|
|
1689
|
+
function parseTimeoutSeconds(timeout) {
|
|
1690
|
+
if (timeout === void 0) return 300;
|
|
1691
|
+
const timeoutSeconds = Number(timeout);
|
|
1692
|
+
if (!Number.isInteger(timeoutSeconds) || timeoutSeconds <= 0) {
|
|
1292
1693
|
throw new ZapierCliValidationError(
|
|
1293
|
-
|
|
1694
|
+
"Timeout must be a positive integer (seconds)."
|
|
1294
1695
|
);
|
|
1295
1696
|
}
|
|
1697
|
+
return timeoutSeconds;
|
|
1698
|
+
}
|
|
1699
|
+
async function promptConfirm({
|
|
1700
|
+
message,
|
|
1701
|
+
defaultValue
|
|
1702
|
+
}) {
|
|
1296
1703
|
const { confirmed } = await inquirer__default.default.prompt([
|
|
1297
|
-
{
|
|
1298
|
-
|
|
1299
|
-
|
|
1704
|
+
{ type: "confirm", name: "confirmed", message, default: defaultValue }
|
|
1705
|
+
]);
|
|
1706
|
+
return confirmed;
|
|
1707
|
+
}
|
|
1708
|
+
function promptlessCredentialResetError(credentials) {
|
|
1709
|
+
throw new ZapierCliValidationError(
|
|
1710
|
+
`Already logged in as "${credentials.name}". Run \`logout\` first or use an interactive terminal to re-authenticate.`
|
|
1711
|
+
);
|
|
1712
|
+
}
|
|
1713
|
+
function promptlessLegacyJwtUpgradeError() {
|
|
1714
|
+
throw new ZapierCliValidationError(
|
|
1715
|
+
"Legacy JWT login detected. Run `logout` first or use an interactive terminal to migrate to client credentials."
|
|
1716
|
+
);
|
|
1717
|
+
}
|
|
1718
|
+
async function clearExistingAuthState({
|
|
1719
|
+
sdk,
|
|
1720
|
+
baseUrl,
|
|
1721
|
+
interactive,
|
|
1722
|
+
entryPoint
|
|
1723
|
+
}) {
|
|
1724
|
+
const activeCredentials = getActiveCredentials({ baseUrl });
|
|
1725
|
+
const flowLabel = getEntryPointLabel(entryPoint);
|
|
1726
|
+
if (activeCredentials) {
|
|
1727
|
+
const confirmed = interactive ? await promptConfirm({
|
|
1728
|
+
defaultValue: false,
|
|
1300
1729
|
message: `You are already logged in as "${activeCredentials.name}".
|
|
1301
1730
|
Logging out will delete these credentials and may interrupt other Zapier SDK or CLI sessions using them.
|
|
1302
|
-
Log out and
|
|
1303
|
-
|
|
1731
|
+
Log out and ${getActiveCredentialsAction(entryPoint)}?`
|
|
1732
|
+
}) : promptlessCredentialResetError(activeCredentials);
|
|
1733
|
+
if (!confirmed) {
|
|
1734
|
+
console.log(`${flowLabel} cancelled.`);
|
|
1735
|
+
return false;
|
|
1304
1736
|
}
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1737
|
+
try {
|
|
1738
|
+
await revokeCredentials({
|
|
1739
|
+
api: sdk.context.api,
|
|
1740
|
+
credentials: activeCredentials
|
|
1741
|
+
});
|
|
1742
|
+
} catch {
|
|
1743
|
+
if (!interactive) {
|
|
1744
|
+
throw new ZapierCliValidationError(
|
|
1745
|
+
`${flowLabel} cleanup failed and cannot be reset without confirmation. Re-run with an interactive terminal.`
|
|
1746
|
+
);
|
|
1747
|
+
}
|
|
1748
|
+
const reset = await promptConfirm({
|
|
1749
|
+
defaultValue: false,
|
|
1750
|
+
message: `${flowLabel} cleanup failed. Reset local session state and continue?`
|
|
1751
|
+
});
|
|
1752
|
+
if (!reset) {
|
|
1753
|
+
console.log(`${flowLabel} cancelled.`);
|
|
1754
|
+
return false;
|
|
1755
|
+
}
|
|
1756
|
+
await deleteStoredClientCredentials({
|
|
1757
|
+
name: activeCredentials.name,
|
|
1758
|
+
baseUrl: activeCredentials.baseUrl
|
|
1759
|
+
});
|
|
1324
1760
|
}
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
}
|
|
1332
|
-
|
|
1333
|
-
if (nonInteractive) {
|
|
1334
|
-
throw new ZapierCliValidationError(
|
|
1335
|
-
"Login cleanup failed and cannot be reset without confirmation. Re-run with an interactive terminal."
|
|
1336
|
-
);
|
|
1337
|
-
}
|
|
1338
|
-
const { confirmed } = await inquirer__default.default.prompt([
|
|
1339
|
-
{
|
|
1340
|
-
type: "confirm",
|
|
1341
|
-
name: "confirmed",
|
|
1342
|
-
message: "Login cleanup failed. Reset local session state and continue?",
|
|
1343
|
-
default: false
|
|
1761
|
+
} else if (hasLegacyJwtConfig()) {
|
|
1762
|
+
const confirmed = interactive ? await promptConfirm({
|
|
1763
|
+
defaultValue: true,
|
|
1764
|
+
message: LEGACY_JWT_UPGRADE_PROMPT
|
|
1765
|
+
}) : promptlessLegacyJwtUpgradeError();
|
|
1766
|
+
if (!confirmed) {
|
|
1767
|
+
console.log(`${flowLabel} cancelled.`);
|
|
1768
|
+
return false;
|
|
1344
1769
|
}
|
|
1345
|
-
]);
|
|
1346
|
-
if (!confirmed) {
|
|
1347
|
-
console.log("Login cancelled.");
|
|
1348
|
-
return false;
|
|
1349
1770
|
}
|
|
1350
1771
|
return true;
|
|
1351
1772
|
}
|
|
1352
|
-
function
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
}
|
|
1357
|
-
return timeoutSeconds;
|
|
1773
|
+
async function getProfile(api2) {
|
|
1774
|
+
return api2.get("/zapier/api/v4/profile/", {
|
|
1775
|
+
authRequired: true
|
|
1776
|
+
});
|
|
1358
1777
|
}
|
|
1359
|
-
async function
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1778
|
+
async function saveClientCredentials({
|
|
1779
|
+
api: api2,
|
|
1780
|
+
name,
|
|
1781
|
+
credentialsBaseUrl,
|
|
1782
|
+
useApprovals,
|
|
1783
|
+
cleanupLogPrefix
|
|
1784
|
+
}) {
|
|
1785
|
+
await setupClientCredentials({
|
|
1786
|
+
api: api2,
|
|
1787
|
+
name,
|
|
1788
|
+
credentialsBaseUrl,
|
|
1789
|
+
...useApprovals && { policy: EMPTY_POLICY }
|
|
1790
|
+
});
|
|
1791
|
+
try {
|
|
1792
|
+
await clearLegacyJwtState();
|
|
1793
|
+
} catch (err) {
|
|
1794
|
+
console.error(
|
|
1795
|
+
`[${cleanupLogPrefix}] Best-effort legacy JWT cleanup failed:`,
|
|
1796
|
+
err
|
|
1797
|
+
);
|
|
1363
1798
|
}
|
|
1364
|
-
const { credentialName } = await inquirer__default.default.prompt([
|
|
1365
|
-
{
|
|
1366
|
-
type: "input",
|
|
1367
|
-
name: "credentialName",
|
|
1368
|
-
message: "Enter a name to identify them:",
|
|
1369
|
-
default: fallback,
|
|
1370
|
-
validate: (input) => {
|
|
1371
|
-
if (!input.trim()) return "Name cannot be empty";
|
|
1372
|
-
return true;
|
|
1373
|
-
}
|
|
1374
|
-
}
|
|
1375
|
-
]);
|
|
1376
|
-
return credentialName;
|
|
1377
1799
|
}
|
|
1378
|
-
function
|
|
1800
|
+
function emitAccountAuthSuccess({
|
|
1379
1801
|
sdk,
|
|
1380
1802
|
profile
|
|
1381
1803
|
}) {
|
|
1382
1804
|
sdk.context.eventEmission.emit(
|
|
1383
1805
|
"platform.sdk.ApplicationLifecycleEvent",
|
|
1384
1806
|
zapierSdk.buildApplicationLifecycleEvent(
|
|
1385
|
-
{
|
|
1386
|
-
lifecycle_event_type: "login_success"
|
|
1387
|
-
},
|
|
1807
|
+
{ lifecycle_event_type: "login_success" },
|
|
1388
1808
|
{
|
|
1389
1809
|
customuser_id: profile.user_id,
|
|
1390
1810
|
account_id: profile.roles[0]?.account_id ?? null
|
|
@@ -1392,18 +1812,128 @@ function emitLoginSuccess({
|
|
|
1392
1812
|
)
|
|
1393
1813
|
);
|
|
1394
1814
|
}
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1815
|
+
function emitSignupSuccess({
|
|
1816
|
+
sdk
|
|
1817
|
+
}) {
|
|
1818
|
+
sdk.context.eventEmission.emit(
|
|
1819
|
+
"platform.sdk.ApplicationLifecycleEvent",
|
|
1820
|
+
zapierSdk.buildApplicationLifecycleEvent({ lifecycle_event_type: "signup_success" })
|
|
1821
|
+
);
|
|
1399
1822
|
}
|
|
1400
|
-
async function
|
|
1823
|
+
async function runOauthWithRedaction(runOauth) {
|
|
1401
1824
|
try {
|
|
1402
|
-
await
|
|
1403
|
-
} catch (
|
|
1404
|
-
|
|
1825
|
+
return await runOauth();
|
|
1826
|
+
} catch (error) {
|
|
1827
|
+
if (error instanceof ZapierCliUserCancellationError) throw error;
|
|
1828
|
+
throw toRedactedOauthError(error);
|
|
1829
|
+
}
|
|
1830
|
+
}
|
|
1831
|
+
async function runOauthForEntryPoint({
|
|
1832
|
+
sdk,
|
|
1833
|
+
entryPoint,
|
|
1834
|
+
timeoutMs,
|
|
1835
|
+
pkceCredentials,
|
|
1836
|
+
baseUrl,
|
|
1837
|
+
headless,
|
|
1838
|
+
interactive
|
|
1839
|
+
}) {
|
|
1840
|
+
if (entryPoint === "signup") {
|
|
1841
|
+
return runOauthWithRedaction(
|
|
1842
|
+
() => runSignupOauthFlow({
|
|
1843
|
+
timeoutMs,
|
|
1844
|
+
pkceCredentials,
|
|
1845
|
+
baseUrl,
|
|
1846
|
+
headless,
|
|
1847
|
+
interactive,
|
|
1848
|
+
recoveryMessage: headless ? HEADLESS_SIGNUP_RECOVERY_MESSAGE : SIGNUP_RECOVERY_MESSAGE,
|
|
1849
|
+
onProgress: (event) => {
|
|
1850
|
+
if (event.type === "callback_accepted") {
|
|
1851
|
+
emitSignupSuccess({ sdk });
|
|
1852
|
+
}
|
|
1853
|
+
}
|
|
1854
|
+
})
|
|
1855
|
+
);
|
|
1405
1856
|
}
|
|
1857
|
+
return runOauthWithRedaction(
|
|
1858
|
+
() => runLoginOauthFlow({ timeoutMs, pkceCredentials, baseUrl })
|
|
1859
|
+
);
|
|
1406
1860
|
}
|
|
1861
|
+
async function runAccountAuth({
|
|
1862
|
+
sdk,
|
|
1863
|
+
options,
|
|
1864
|
+
entryPoint
|
|
1865
|
+
}) {
|
|
1866
|
+
const timeoutSeconds = parseTimeoutSeconds(options.timeout);
|
|
1867
|
+
const interactive = !resolveNonInteractive(options);
|
|
1868
|
+
const resolvedCredentials = await sdk.context.resolveCredentials();
|
|
1869
|
+
const pkceCredentials = toPkceCredentials(resolvedCredentials);
|
|
1870
|
+
const credentialsBaseUrl = await resolveCredentialsBaseUrl({
|
|
1871
|
+
...sdk.context,
|
|
1872
|
+
resolvedCredentials
|
|
1873
|
+
});
|
|
1874
|
+
if (!await clearExistingAuthState({
|
|
1875
|
+
sdk,
|
|
1876
|
+
baseUrl: credentialsBaseUrl,
|
|
1877
|
+
interactive,
|
|
1878
|
+
entryPoint
|
|
1879
|
+
})) {
|
|
1880
|
+
return;
|
|
1881
|
+
}
|
|
1882
|
+
const { accessToken } = await runOauthForEntryPoint({
|
|
1883
|
+
sdk,
|
|
1884
|
+
entryPoint,
|
|
1885
|
+
timeoutMs: timeoutSeconds * 1e3,
|
|
1886
|
+
pkceCredentials,
|
|
1887
|
+
baseUrl: credentialsBaseUrl,
|
|
1888
|
+
headless: options.headless === true,
|
|
1889
|
+
interactive
|
|
1890
|
+
});
|
|
1891
|
+
const scopedApi = zapierSdk.getOrCreateApiClient({
|
|
1892
|
+
credentials: accessToken,
|
|
1893
|
+
baseUrl: credentialsBaseUrl
|
|
1894
|
+
});
|
|
1895
|
+
const profile = await getProfile(scopedApi);
|
|
1896
|
+
console.log(getProfileMessage(entryPoint, profile.email));
|
|
1897
|
+
console.log(
|
|
1898
|
+
"\nGenerating credentials so this machine can make authenticated requests on your behalf."
|
|
1899
|
+
);
|
|
1900
|
+
const resolveCredentialsName = interactive ? ({ email }) => promptCredentialsName({
|
|
1901
|
+
email,
|
|
1902
|
+
promptMessage: getCredentialsPromptMessage(entryPoint)
|
|
1903
|
+
}) : resolveDefaultCredentialsName;
|
|
1904
|
+
const credentialName = await resolveCredentialsName({ email: profile.email });
|
|
1905
|
+
const useApprovals = options.useApprovals === true;
|
|
1906
|
+
await saveClientCredentials({
|
|
1907
|
+
api: scopedApi,
|
|
1908
|
+
name: credentialName,
|
|
1909
|
+
credentialsBaseUrl,
|
|
1910
|
+
useApprovals,
|
|
1911
|
+
cleanupLogPrefix: entryPoint
|
|
1912
|
+
});
|
|
1913
|
+
console.log(
|
|
1914
|
+
`\u2705 Credentials "${credentialName}" created and set as default. You are ready to use the Zapier SDK.`
|
|
1915
|
+
);
|
|
1916
|
+
if (useApprovals) {
|
|
1917
|
+
console.log("\u{1F510} Approvals are enabled for these credentials.");
|
|
1918
|
+
}
|
|
1919
|
+
emitAccountAuthSuccess({ sdk, profile });
|
|
1920
|
+
}
|
|
1921
|
+
var LoginSchema = zod.z.object({
|
|
1922
|
+
timeout: zod.z.string().optional().describe("Login timeout in seconds (default: 300)"),
|
|
1923
|
+
useApprovals: zod.z.boolean().optional().describe(
|
|
1924
|
+
"Require approvals for actions performed with these credentials"
|
|
1925
|
+
),
|
|
1926
|
+
nonInteractive: zod.z.boolean().optional().describe(
|
|
1927
|
+
"Skip interactive prompts. Uses defaults where possible; errors instead of prompting when input is required. Useful in CI, piped output, or environments where TTY detection is unreliable."
|
|
1928
|
+
),
|
|
1929
|
+
/** @deprecated Use `nonInteractive` instead. */
|
|
1930
|
+
skipPrompts: zod.z.boolean().optional().meta({
|
|
1931
|
+
deprecated: true,
|
|
1932
|
+
deprecationMessage: "Use --non-interactive instead."
|
|
1933
|
+
})
|
|
1934
|
+
}).describe("Log in to Zapier to access your account");
|
|
1935
|
+
|
|
1936
|
+
// src/plugins/login/index.ts
|
|
1407
1937
|
var loginPlugin = zapierSdk.definePlugin(
|
|
1408
1938
|
(sdk) => zapierSdk.createPluginMethod(sdk, {
|
|
1409
1939
|
name: "login",
|
|
@@ -1411,68 +1941,37 @@ var loginPlugin = zapierSdk.definePlugin(
|
|
|
1411
1941
|
inputSchema: LoginSchema,
|
|
1412
1942
|
supportsJsonOutput: false,
|
|
1413
1943
|
handler: async ({ sdk: sdk2, options }) => {
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
pkceCredentials,
|
|
1446
|
-
baseUrl: credentialsBaseUrl
|
|
1447
|
-
});
|
|
1448
|
-
const scopedApi = zapierSdk.getOrCreateApiClient({
|
|
1449
|
-
credentials: accessToken,
|
|
1450
|
-
baseUrl: credentialsBaseUrl
|
|
1451
|
-
});
|
|
1452
|
-
const profile = await getProfile(scopedApi);
|
|
1453
|
-
console.log(`\u{1F464} Logged in as ${profile.email}`);
|
|
1454
|
-
console.log(
|
|
1455
|
-
"\nGenerating credentials so this machine can make authenticated requests on your behalf."
|
|
1456
|
-
);
|
|
1457
|
-
const credentialName = await promptCredentialsName(
|
|
1458
|
-
profile.email,
|
|
1459
|
-
nonInteractive
|
|
1460
|
-
);
|
|
1461
|
-
const useApprovals = options.useApprovals === true;
|
|
1462
|
-
await setupClientCredentials({
|
|
1463
|
-
api: scopedApi,
|
|
1464
|
-
name: credentialName,
|
|
1465
|
-
credentialsBaseUrl,
|
|
1466
|
-
...useApprovals && { policy: EMPTY_POLICY }
|
|
1467
|
-
});
|
|
1468
|
-
await bestEffortClearLegacyJwtState();
|
|
1469
|
-
console.log(
|
|
1470
|
-
`\u2705 Credentials "${credentialName}" created and set as default. You are ready to use the Zapier SDK.`
|
|
1471
|
-
);
|
|
1472
|
-
if (useApprovals) {
|
|
1473
|
-
console.log("\u{1F510} Approvals are enabled for these credentials.");
|
|
1474
|
-
}
|
|
1475
|
-
emitLoginSuccess({ sdk: sdk2, profile });
|
|
1944
|
+
await runAccountAuth({ sdk: sdk2, options, entryPoint: "login" });
|
|
1945
|
+
}
|
|
1946
|
+
})
|
|
1947
|
+
);
|
|
1948
|
+
var SignupSchema = zod.z.object({
|
|
1949
|
+
timeout: zod.z.string().optional().describe("Signup timeout in seconds (default: 300)"),
|
|
1950
|
+
useApprovals: zod.z.boolean().optional().describe(
|
|
1951
|
+
"Require approvals for actions performed with these credentials"
|
|
1952
|
+
),
|
|
1953
|
+
nonInteractive: zod.z.boolean().optional().describe(
|
|
1954
|
+
"Skip interactive prompts. Uses defaults where possible; errors instead of prompting when input is required. Useful in CI, piped output, or environments where TTY detection is unreliable."
|
|
1955
|
+
),
|
|
1956
|
+
/** @deprecated Use `nonInteractive` instead. */
|
|
1957
|
+
skipPrompts: zod.z.boolean().optional().meta({
|
|
1958
|
+
deprecated: true,
|
|
1959
|
+
deprecationMessage: "Use --non-interactive instead."
|
|
1960
|
+
}),
|
|
1961
|
+
headless: zod.z.boolean().optional().describe(
|
|
1962
|
+
"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."
|
|
1963
|
+
)
|
|
1964
|
+
}).describe("Set up Zapier account access and SDK credentials");
|
|
1965
|
+
|
|
1966
|
+
// src/plugins/signup/index.ts
|
|
1967
|
+
var signupPlugin = zapierSdk.definePlugin(
|
|
1968
|
+
(sdk) => zapierSdk.createPluginMethod(sdk, {
|
|
1969
|
+
name: "signup",
|
|
1970
|
+
categories: ["account"],
|
|
1971
|
+
inputSchema: SignupSchema,
|
|
1972
|
+
supportsJsonOutput: false,
|
|
1973
|
+
handler: async ({ sdk: sdk2, options }) => {
|
|
1974
|
+
await runAccountAuth({ sdk: sdk2, options, entryPoint: "signup" });
|
|
1476
1975
|
}
|
|
1477
1976
|
})
|
|
1478
1977
|
);
|
|
@@ -4004,7 +4503,7 @@ var watchTriggerInboxCliPlugin = zapierSdk.definePlugin(
|
|
|
4004
4503
|
// package.json with { type: 'json' }
|
|
4005
4504
|
var package_default = {
|
|
4006
4505
|
name: "@zapier/zapier-sdk-cli",
|
|
4007
|
-
version: "0.
|
|
4506
|
+
version: "0.53.1"};
|
|
4008
4507
|
|
|
4009
4508
|
// src/experimental.ts
|
|
4010
4509
|
experimental.injectCliLogin(login_exports);
|
|
@@ -4020,7 +4519,7 @@ function createZapierCliSdk(options = {}) {
|
|
|
4020
4519
|
...sdkOptions,
|
|
4021
4520
|
eventEmission: { ...sdkOptions.eventEmission, callContext: "cli" },
|
|
4022
4521
|
callerPackage: { name: package_default.name, version: package_default.version }
|
|
4023
|
-
}).use(extensionsContextPlugin).use(experimentalContextPlugin).use(generateAppTypesPlugin).use(buildManifestPlugin).use(bundleCodePlugin).use(getLoginConfigPathPlugin).use(addAppsPlugin).use(feedbackPlugin).use(curlPlugin).use(initPlugin).use(drainTriggerInboxCliPlugin, { override: true }).use(watchTriggerInboxCliPlugin, { override: true }).use(mcpPlugin).use(loginPlugin).use(logoutPlugin).use(cliOverridesPlugin, { override: true }).toSdk();
|
|
4522
|
+
}).use(extensionsContextPlugin).use(experimentalContextPlugin).use(generateAppTypesPlugin).use(buildManifestPlugin).use(bundleCodePlugin).use(getLoginConfigPathPlugin).use(addAppsPlugin).use(feedbackPlugin).use(curlPlugin).use(initPlugin).use(drainTriggerInboxCliPlugin, { override: true }).use(watchTriggerInboxCliPlugin, { override: true }).use(mcpPlugin).use(loginPlugin).use(signupPlugin).use(logoutPlugin).use(cliOverridesPlugin, { override: true }).toSdk();
|
|
4024
4523
|
for (const ext of extensions) {
|
|
4025
4524
|
try {
|
|
4026
4525
|
experimental.addPlugin(sdk, ext);
|