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