@zapier/zapier-sdk-cli 0.42.1 → 0.43.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +21 -0
- package/README.md +4 -4
- package/dist/cli.cjs +619 -34
- package/dist/cli.d.mts +0 -1
- package/dist/cli.d.ts +0 -1
- package/dist/cli.mjs +601 -19
- package/dist/index.cjs +607 -23
- package/dist/index.mjs +592 -11
- package/dist/login.cjs +599 -7
- package/dist/login.d.mts +144 -1
- package/dist/login.d.ts +144 -1
- package/dist/login.mjs +565 -1
- package/dist/package.json +6 -3
- package/dist/src/login/filesystem-cache.d.ts +25 -0
- package/dist/src/login/filesystem-cache.js +195 -0
- package/dist/src/login/index.d.ts +115 -0
- package/dist/src/login/index.js +442 -0
- package/dist/src/login/keychain.d.ts +18 -0
- package/dist/src/login/keychain.js +74 -0
- package/dist/src/login.d.ts +10 -1
- package/dist/src/login.js +10 -1
- package/dist/src/plugins/feedback/index.js +1 -1
- package/dist/src/plugins/getLoginConfigPath/index.js +1 -1
- package/dist/src/plugins/login/index.js +1 -1
- package/dist/src/plugins/logout/index.js +1 -1
- package/dist/src/sdk.js +1 -1
- package/dist/src/utils/auth/login.d.ts +1 -1
- package/dist/src/utils/auth/login.js +1 -1
- package/dist/src/utils/constants.d.ts +1 -1
- package/dist/src/utils/constants.js +1 -1
- package/dist/src/utils/version-checker.js +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +8 -5
package/dist/index.cjs
CHANGED
|
@@ -1,9 +1,14 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var
|
|
3
|
+
var Conf = require('conf');
|
|
4
|
+
var fs = require('fs');
|
|
5
|
+
var jwt = require('jsonwebtoken');
|
|
6
|
+
var crossKeychain = require('cross-keychain');
|
|
7
|
+
var crypto = require('crypto');
|
|
8
|
+
var path = require('path');
|
|
9
|
+
var lockfile = require('proper-lockfile');
|
|
4
10
|
var zapierSdk = require('@zapier/zapier-sdk');
|
|
5
11
|
var open = require('open');
|
|
6
|
-
var crypto = require('crypto');
|
|
7
12
|
var express = require('express');
|
|
8
13
|
var pkceChallenge = require('pkce-challenge');
|
|
9
14
|
var ora = require('ora');
|
|
@@ -12,8 +17,6 @@ var inquirer = require('inquirer');
|
|
|
12
17
|
var zod = require('zod');
|
|
13
18
|
var zapierSdkMcp = require('@zapier/zapier-sdk-mcp');
|
|
14
19
|
var esbuild = require('esbuild');
|
|
15
|
-
var fs = require('fs');
|
|
16
|
-
var path = require('path');
|
|
17
20
|
var promises = require('fs/promises');
|
|
18
21
|
var ts = require('typescript');
|
|
19
22
|
require('is-installed-globally');
|
|
@@ -42,20 +45,601 @@ function _interopNamespace(e) {
|
|
|
42
45
|
return Object.freeze(n);
|
|
43
46
|
}
|
|
44
47
|
|
|
45
|
-
var
|
|
46
|
-
var
|
|
48
|
+
var Conf__default = /*#__PURE__*/_interopDefault(Conf);
|
|
49
|
+
var fs__namespace = /*#__PURE__*/_interopNamespace(fs);
|
|
50
|
+
var jwt__namespace = /*#__PURE__*/_interopNamespace(jwt);
|
|
47
51
|
var crypto__default = /*#__PURE__*/_interopDefault(crypto);
|
|
52
|
+
var path__namespace = /*#__PURE__*/_interopNamespace(path);
|
|
53
|
+
var lockfile__namespace = /*#__PURE__*/_interopNamespace(lockfile);
|
|
54
|
+
var open__default = /*#__PURE__*/_interopDefault(open);
|
|
48
55
|
var express__default = /*#__PURE__*/_interopDefault(express);
|
|
49
56
|
var pkceChallenge__default = /*#__PURE__*/_interopDefault(pkceChallenge);
|
|
50
57
|
var ora__default = /*#__PURE__*/_interopDefault(ora);
|
|
51
58
|
var chalk3__default = /*#__PURE__*/_interopDefault(chalk3);
|
|
52
59
|
var inquirer__default = /*#__PURE__*/_interopDefault(inquirer);
|
|
53
|
-
var fs__namespace = /*#__PURE__*/_interopNamespace(fs);
|
|
54
|
-
var path__namespace = /*#__PURE__*/_interopNamespace(path);
|
|
55
60
|
var ts__namespace = /*#__PURE__*/_interopNamespace(ts);
|
|
56
61
|
var Handlebars__default = /*#__PURE__*/_interopDefault(Handlebars);
|
|
57
62
|
|
|
58
|
-
|
|
63
|
+
var __defProp = Object.defineProperty;
|
|
64
|
+
var __export = (target, all) => {
|
|
65
|
+
for (var name in all)
|
|
66
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
// src/login.ts
|
|
70
|
+
var login_exports = {};
|
|
71
|
+
__export(login_exports, {
|
|
72
|
+
AUTH_MODE_HEADER: () => AUTH_MODE_HEADER,
|
|
73
|
+
ZapierAuthenticationError: () => ZapierAuthenticationError,
|
|
74
|
+
createCache: () => createCache,
|
|
75
|
+
getAuthAuthorizeUrl: () => getAuthAuthorizeUrl,
|
|
76
|
+
getAuthTokenUrl: () => getAuthTokenUrl,
|
|
77
|
+
getConfig: () => getConfig,
|
|
78
|
+
getConfigPath: () => getConfigPath,
|
|
79
|
+
getLoggedInUser: () => getLoggedInUser,
|
|
80
|
+
getLoginStorageMode: () => getLoginStorageMode,
|
|
81
|
+
getPkceLoginConfig: () => getPkceLoginConfig,
|
|
82
|
+
getToken: () => getToken,
|
|
83
|
+
logout: () => logout,
|
|
84
|
+
unloadConfig: () => unloadConfig,
|
|
85
|
+
updateLogin: () => updateLogin
|
|
86
|
+
});
|
|
87
|
+
var SERVICE = "zapier-sdk-cli";
|
|
88
|
+
var ACCOUNT = "login";
|
|
89
|
+
var cachedBackendInfo;
|
|
90
|
+
async function getBackendInfo() {
|
|
91
|
+
if (!cachedBackendInfo) {
|
|
92
|
+
const keyring = await crossKeychain.getKeyring();
|
|
93
|
+
cachedBackendInfo = `${keyring.name} (${keyring.id})`;
|
|
94
|
+
}
|
|
95
|
+
return cachedBackendInfo;
|
|
96
|
+
}
|
|
97
|
+
var keychainQueue = Promise.resolve();
|
|
98
|
+
function enqueue(fn) {
|
|
99
|
+
const result = keychainQueue.then(fn, fn);
|
|
100
|
+
keychainQueue = result.then(
|
|
101
|
+
() => {
|
|
102
|
+
},
|
|
103
|
+
() => {
|
|
104
|
+
}
|
|
105
|
+
);
|
|
106
|
+
return result;
|
|
107
|
+
}
|
|
108
|
+
async function getTokensFromKeychain({
|
|
109
|
+
debugLog
|
|
110
|
+
} = {}) {
|
|
111
|
+
return enqueue(async () => {
|
|
112
|
+
const backendInfo = await getBackendInfo();
|
|
113
|
+
debugLog?.(`Keychain read via ${backendInfo}`);
|
|
114
|
+
const startTime = Date.now();
|
|
115
|
+
const raw = await crossKeychain.getPassword(SERVICE, ACCOUNT);
|
|
116
|
+
debugLog?.(`Keychain read completed in ${Date.now() - startTime}ms`);
|
|
117
|
+
if (!raw) {
|
|
118
|
+
debugLog?.("Keychain returned no data");
|
|
119
|
+
return void 0;
|
|
120
|
+
}
|
|
121
|
+
let parsed;
|
|
122
|
+
try {
|
|
123
|
+
parsed = JSON.parse(raw);
|
|
124
|
+
} catch {
|
|
125
|
+
debugLog?.("Keychain data is not valid JSON");
|
|
126
|
+
return void 0;
|
|
127
|
+
}
|
|
128
|
+
if (typeof parsed.login_jwt === "string" && typeof parsed.login_refresh_token === "string") {
|
|
129
|
+
return {
|
|
130
|
+
login_jwt: parsed.login_jwt,
|
|
131
|
+
login_refresh_token: parsed.login_refresh_token
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
debugLog?.("Keychain data has invalid shape", parsed);
|
|
135
|
+
return void 0;
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
async function setTokensInKeychain({
|
|
139
|
+
data,
|
|
140
|
+
debugLog
|
|
141
|
+
}) {
|
|
142
|
+
return enqueue(async () => {
|
|
143
|
+
const backendInfo = await getBackendInfo();
|
|
144
|
+
debugLog?.(`Keychain write via ${backendInfo}`);
|
|
145
|
+
const startTime = Date.now();
|
|
146
|
+
await crossKeychain.setPassword(SERVICE, ACCOUNT, JSON.stringify(data));
|
|
147
|
+
debugLog?.(`Keychain write completed in ${Date.now() - startTime}ms`);
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
async function clearTokensFromKeychain({
|
|
151
|
+
debugLog
|
|
152
|
+
} = {}) {
|
|
153
|
+
return enqueue(async () => {
|
|
154
|
+
try {
|
|
155
|
+
const backendInfo = await getBackendInfo();
|
|
156
|
+
debugLog?.(`Keychain clear via ${backendInfo}`);
|
|
157
|
+
await crossKeychain.deletePassword(SERVICE, ACCOUNT);
|
|
158
|
+
} catch {
|
|
159
|
+
}
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
var SERVICE2 = "zapier-sdk-cache";
|
|
163
|
+
var CONFIG_KEY = "cache";
|
|
164
|
+
var LOCK_UPDATE_MS = 5e3;
|
|
165
|
+
var LOCK_STALE_MS = 1e4;
|
|
166
|
+
var LOCK_RETRY_WAIT_MS = 100;
|
|
167
|
+
var LOCK_RETRY_MAX_WAIT_MS = 1e3;
|
|
168
|
+
var LOCK_RETRY_COUNT = 120;
|
|
169
|
+
function keychainAccount(key) {
|
|
170
|
+
return crypto.createHash("sha256").update(key).digest("hex");
|
|
171
|
+
}
|
|
172
|
+
function readConfigMap() {
|
|
173
|
+
const cfg = getConfig();
|
|
174
|
+
const stored = cfg.get(CONFIG_KEY);
|
|
175
|
+
if (stored && typeof stored === "object") {
|
|
176
|
+
return stored;
|
|
177
|
+
}
|
|
178
|
+
return {};
|
|
179
|
+
}
|
|
180
|
+
function writeConfigMap(map) {
|
|
181
|
+
getConfig().set(CONFIG_KEY, map);
|
|
182
|
+
}
|
|
183
|
+
function entryIsExpired(entry) {
|
|
184
|
+
return entry.expires_at !== void 0 && entry.expires_at <= Date.now();
|
|
185
|
+
}
|
|
186
|
+
function createCache() {
|
|
187
|
+
return {
|
|
188
|
+
async get(key) {
|
|
189
|
+
const entry = readConfigMap()[key];
|
|
190
|
+
if (!entry) return void 0;
|
|
191
|
+
if (entryIsExpired(entry)) return void 0;
|
|
192
|
+
if (entry.secret) {
|
|
193
|
+
const stored = await enqueue(async () => {
|
|
194
|
+
await getBackendInfo();
|
|
195
|
+
return crossKeychain.getPassword(SERVICE2, keychainAccount(key));
|
|
196
|
+
});
|
|
197
|
+
if (!stored) {
|
|
198
|
+
return void 0;
|
|
199
|
+
}
|
|
200
|
+
return { value: stored, expiresAt: entry.expires_at };
|
|
201
|
+
}
|
|
202
|
+
if (entry.value === void 0) return void 0;
|
|
203
|
+
return { value: entry.value, expiresAt: entry.expires_at };
|
|
204
|
+
},
|
|
205
|
+
async set(key, value, options) {
|
|
206
|
+
const secret = options?.secret ?? false;
|
|
207
|
+
const expiresAt = options?.ttl ? Date.now() + options.ttl * 1e3 : void 0;
|
|
208
|
+
if (secret) {
|
|
209
|
+
try {
|
|
210
|
+
await enqueue(async () => {
|
|
211
|
+
await getBackendInfo();
|
|
212
|
+
await crossKeychain.setPassword(SERVICE2, keychainAccount(key), value);
|
|
213
|
+
});
|
|
214
|
+
} catch {
|
|
215
|
+
return;
|
|
216
|
+
}
|
|
217
|
+
const map = readConfigMap();
|
|
218
|
+
map[key] = { secret: true, expires_at: expiresAt };
|
|
219
|
+
try {
|
|
220
|
+
writeConfigMap(map);
|
|
221
|
+
} catch {
|
|
222
|
+
}
|
|
223
|
+
} else {
|
|
224
|
+
const map = readConfigMap();
|
|
225
|
+
map[key] = { secret: false, value, expires_at: expiresAt };
|
|
226
|
+
try {
|
|
227
|
+
writeConfigMap(map);
|
|
228
|
+
} catch {
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
},
|
|
232
|
+
async delete(key) {
|
|
233
|
+
const map = readConfigMap();
|
|
234
|
+
const entry = map[key];
|
|
235
|
+
if (entry) {
|
|
236
|
+
delete map[key];
|
|
237
|
+
try {
|
|
238
|
+
writeConfigMap(map);
|
|
239
|
+
} catch {
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
if (entry?.secret) {
|
|
243
|
+
try {
|
|
244
|
+
await enqueue(async () => {
|
|
245
|
+
await getBackendInfo();
|
|
246
|
+
await crossKeychain.deletePassword(SERVICE2, keychainAccount(key));
|
|
247
|
+
});
|
|
248
|
+
} catch {
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
},
|
|
252
|
+
async withLock(_key, fn) {
|
|
253
|
+
const cfg = getConfig();
|
|
254
|
+
const lockTarget = `${cfg.path}.cache-lock`;
|
|
255
|
+
try {
|
|
256
|
+
fs.mkdirSync(path.dirname(lockTarget), { recursive: true });
|
|
257
|
+
if (!fs.existsSync(lockTarget)) {
|
|
258
|
+
fs.writeFileSync(lockTarget, "");
|
|
259
|
+
}
|
|
260
|
+
} catch {
|
|
261
|
+
return fn();
|
|
262
|
+
}
|
|
263
|
+
let release = null;
|
|
264
|
+
try {
|
|
265
|
+
release = await lockfile__namespace.lock(lockTarget, {
|
|
266
|
+
stale: LOCK_STALE_MS,
|
|
267
|
+
update: LOCK_UPDATE_MS,
|
|
268
|
+
retries: {
|
|
269
|
+
retries: LOCK_RETRY_COUNT,
|
|
270
|
+
factor: 1.2,
|
|
271
|
+
minTimeout: LOCK_RETRY_WAIT_MS,
|
|
272
|
+
maxTimeout: LOCK_RETRY_MAX_WAIT_MS
|
|
273
|
+
}
|
|
274
|
+
});
|
|
275
|
+
} catch {
|
|
276
|
+
return fn();
|
|
277
|
+
}
|
|
278
|
+
try {
|
|
279
|
+
return await fn();
|
|
280
|
+
} finally {
|
|
281
|
+
try {
|
|
282
|
+
await release();
|
|
283
|
+
} catch {
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
};
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
// src/login/index.ts
|
|
291
|
+
var ZapierAuthenticationError = class extends Error {
|
|
292
|
+
constructor(message) {
|
|
293
|
+
super(message);
|
|
294
|
+
this.name = "ZapierAuthenticationError";
|
|
295
|
+
}
|
|
296
|
+
};
|
|
297
|
+
var config = null;
|
|
298
|
+
var DEFAULT_AUTH_CLIENT_ID = "grwWZD5hUWGvb4V8ODBuOtXer3h0DBEZ2HR8aay6";
|
|
299
|
+
var TOKEN_REFRESH_BUFFER_MS = 5 * 60 * 1e3;
|
|
300
|
+
function createDebugLog(enabled) {
|
|
301
|
+
if (!enabled) {
|
|
302
|
+
return () => {
|
|
303
|
+
};
|
|
304
|
+
}
|
|
305
|
+
return (message, data) => {
|
|
306
|
+
if (data === void 0) {
|
|
307
|
+
console.log(`[Zapier SDK CLI Login] ${message}`);
|
|
308
|
+
} else {
|
|
309
|
+
console.log(`[Zapier SDK CLI Login] ${message}`, data);
|
|
310
|
+
}
|
|
311
|
+
};
|
|
312
|
+
}
|
|
313
|
+
function censorHeaderValue(value) {
|
|
314
|
+
if (value.length > 12) {
|
|
315
|
+
return `${value.substring(0, 4)}...${value.substring(value.length - 4)}`;
|
|
316
|
+
}
|
|
317
|
+
return `${value.charAt(0)}...`;
|
|
318
|
+
}
|
|
319
|
+
function getAuthClientId(clientId) {
|
|
320
|
+
return clientId || DEFAULT_AUTH_CLIENT_ID;
|
|
321
|
+
}
|
|
322
|
+
var AUTH_MODE_HEADER = "X-Auth";
|
|
323
|
+
var DEFAULT_AUTH_BASE_URL = "https://zapier.com";
|
|
324
|
+
function getAuthTokenUrl(options) {
|
|
325
|
+
const authBaseUrl = options?.baseUrl || DEFAULT_AUTH_BASE_URL;
|
|
326
|
+
return `${authBaseUrl}/oauth/token/`;
|
|
327
|
+
}
|
|
328
|
+
function getAuthAuthorizeUrl(options) {
|
|
329
|
+
const authBaseUrl = options?.baseUrl || DEFAULT_AUTH_BASE_URL;
|
|
330
|
+
return `${authBaseUrl}/oauth/authorize/`;
|
|
331
|
+
}
|
|
332
|
+
function getPkceLoginConfig(options) {
|
|
333
|
+
return {
|
|
334
|
+
clientId: getAuthClientId(options?.credentials?.clientId),
|
|
335
|
+
tokenUrl: getAuthTokenUrl({ baseUrl: options?.credentials?.baseUrl }),
|
|
336
|
+
authorizeUrl: getAuthAuthorizeUrl({
|
|
337
|
+
baseUrl: options?.credentials?.baseUrl
|
|
338
|
+
})
|
|
339
|
+
};
|
|
340
|
+
}
|
|
341
|
+
var cachedLogin;
|
|
342
|
+
function getConfig() {
|
|
343
|
+
if (!config) {
|
|
344
|
+
config = new Conf__default.default({ projectName: "zapier-sdk-cli" });
|
|
345
|
+
if (!config.has("login_storage_mode")) {
|
|
346
|
+
config.set(
|
|
347
|
+
"login_storage_mode",
|
|
348
|
+
fs.existsSync(config.path) ? "config" : "keychain"
|
|
349
|
+
);
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
return config;
|
|
353
|
+
}
|
|
354
|
+
function unloadConfig() {
|
|
355
|
+
config = null;
|
|
356
|
+
cachedLogin = void 0;
|
|
357
|
+
}
|
|
358
|
+
async function updateLogin(loginData, options = {}) {
|
|
359
|
+
const debugLog = createDebugLog(options.debug ?? false);
|
|
360
|
+
const storage = options.storage ?? cachedLogin?.storage ?? "keychain";
|
|
361
|
+
const expiresAt = Date.now() + loginData.expires_in * 1e3;
|
|
362
|
+
const cfg = getConfig();
|
|
363
|
+
cfg.set("login_storage_mode", storage);
|
|
364
|
+
if (storage === "keychain") {
|
|
365
|
+
await setTokensInKeychain({
|
|
366
|
+
data: {
|
|
367
|
+
login_jwt: loginData.access_token,
|
|
368
|
+
login_refresh_token: loginData.refresh_token
|
|
369
|
+
},
|
|
370
|
+
debugLog
|
|
371
|
+
});
|
|
372
|
+
cfg.set("login_expires_at", expiresAt);
|
|
373
|
+
cfg.delete("login_jwt");
|
|
374
|
+
cfg.delete("login_refresh_token");
|
|
375
|
+
} else {
|
|
376
|
+
cfg.set("login_jwt", loginData.access_token);
|
|
377
|
+
cfg.set("login_refresh_token", loginData.refresh_token);
|
|
378
|
+
cfg.set("login_expires_at", expiresAt);
|
|
379
|
+
await clearTokensFromKeychain({ debugLog });
|
|
380
|
+
}
|
|
381
|
+
cachedLogin = {
|
|
382
|
+
jwt: loginData.access_token,
|
|
383
|
+
refreshToken: loginData.refresh_token,
|
|
384
|
+
expiresAt,
|
|
385
|
+
storage
|
|
386
|
+
};
|
|
387
|
+
}
|
|
388
|
+
function decodeJwtOrThrow(token) {
|
|
389
|
+
if (typeof token !== "string") {
|
|
390
|
+
throw new Error("Expected JWT to be a string");
|
|
391
|
+
}
|
|
392
|
+
const decodedJwt = jwt__namespace.decode(token, { complete: true });
|
|
393
|
+
if (!decodedJwt) {
|
|
394
|
+
throw new Error("Could not decode JWT");
|
|
395
|
+
}
|
|
396
|
+
if (typeof decodedJwt.payload === "string") {
|
|
397
|
+
throw new Error("Did not expect JWT payload to be a string");
|
|
398
|
+
}
|
|
399
|
+
return decodedJwt;
|
|
400
|
+
}
|
|
401
|
+
async function refreshJwt(refreshToken, options = {}) {
|
|
402
|
+
const {
|
|
403
|
+
onEvent,
|
|
404
|
+
fetch: fetch2 = globalThis.fetch,
|
|
405
|
+
credentials,
|
|
406
|
+
debug = false
|
|
407
|
+
} = options;
|
|
408
|
+
const debugLog = createDebugLog(debug);
|
|
409
|
+
const tokenUrl = getAuthTokenUrl({ baseUrl: credentials?.baseUrl });
|
|
410
|
+
const clientId = getAuthClientId(credentials?.clientId);
|
|
411
|
+
const startTime = Date.now();
|
|
412
|
+
try {
|
|
413
|
+
onEvent?.({
|
|
414
|
+
type: "auth_refreshing",
|
|
415
|
+
payload: {
|
|
416
|
+
message: "Refreshing your token...",
|
|
417
|
+
operation: "token_refresh"
|
|
418
|
+
},
|
|
419
|
+
timestamp: Date.now()
|
|
420
|
+
});
|
|
421
|
+
debugLog(`\u2192 POST ${tokenUrl}`, {
|
|
422
|
+
headers: {
|
|
423
|
+
"Content-Type": "application/x-www-form-urlencoded",
|
|
424
|
+
[AUTH_MODE_HEADER]: "no"
|
|
425
|
+
},
|
|
426
|
+
body: {
|
|
427
|
+
client_id: clientId,
|
|
428
|
+
refresh_token: censorHeaderValue(refreshToken),
|
|
429
|
+
grant_type: "refresh_token"
|
|
430
|
+
}
|
|
431
|
+
});
|
|
432
|
+
const response = await fetch2(tokenUrl, {
|
|
433
|
+
method: "POST",
|
|
434
|
+
headers: {
|
|
435
|
+
"Content-Type": "application/x-www-form-urlencoded",
|
|
436
|
+
[AUTH_MODE_HEADER]: "no"
|
|
437
|
+
},
|
|
438
|
+
body: new URLSearchParams({
|
|
439
|
+
client_id: clientId,
|
|
440
|
+
refresh_token: refreshToken,
|
|
441
|
+
grant_type: "refresh_token"
|
|
442
|
+
})
|
|
443
|
+
});
|
|
444
|
+
const duration = Date.now() - startTime;
|
|
445
|
+
if (!response.ok) {
|
|
446
|
+
debugLog(`\u2190 ${response.status} ${response.statusText} (${duration}ms)`);
|
|
447
|
+
throw new Error(
|
|
448
|
+
`Token refresh failed: ${response.status} ${response.statusText}`
|
|
449
|
+
);
|
|
450
|
+
}
|
|
451
|
+
debugLog(`\u2190 ${response.status} ${response.statusText} (${duration}ms)`);
|
|
452
|
+
const data = await response.json();
|
|
453
|
+
await updateLogin(data, { debug });
|
|
454
|
+
debugLog(
|
|
455
|
+
`Token refreshed and saved to ${cachedLogin?.storage ?? "keychain"}`
|
|
456
|
+
);
|
|
457
|
+
onEvent?.({
|
|
458
|
+
type: "auth_success",
|
|
459
|
+
payload: {
|
|
460
|
+
message: "Token refreshed successfully",
|
|
461
|
+
operation: "token_refresh"
|
|
462
|
+
},
|
|
463
|
+
timestamp: Date.now()
|
|
464
|
+
});
|
|
465
|
+
return data.access_token;
|
|
466
|
+
} catch (error) {
|
|
467
|
+
const duration = Date.now() - startTime;
|
|
468
|
+
debugLog(`\u2716 Token refresh failed (${duration}ms)`, {
|
|
469
|
+
error: error instanceof Error ? error.message : error
|
|
470
|
+
});
|
|
471
|
+
cachedLogin = void 0;
|
|
472
|
+
const errorMessage = `Token refresh failed: ${error instanceof Error ? error.message : "Unknown error"}`;
|
|
473
|
+
onEvent?.({
|
|
474
|
+
type: "auth_error",
|
|
475
|
+
payload: {
|
|
476
|
+
message: errorMessage,
|
|
477
|
+
error: errorMessage,
|
|
478
|
+
operation: "token_refresh"
|
|
479
|
+
},
|
|
480
|
+
timestamp: Date.now()
|
|
481
|
+
});
|
|
482
|
+
throw error;
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
var pendingRefresh = null;
|
|
486
|
+
var pendingResolve = null;
|
|
487
|
+
async function resolveStoredLogin(debugLog) {
|
|
488
|
+
if (cachedLogin) {
|
|
489
|
+
debugLog("Using in-memory cached credentials");
|
|
490
|
+
return cachedLogin;
|
|
491
|
+
}
|
|
492
|
+
if (pendingResolve) {
|
|
493
|
+
debugLog("Waiting for existing keychain read to complete");
|
|
494
|
+
return pendingResolve;
|
|
495
|
+
}
|
|
496
|
+
pendingResolve = resolveStoredLoginFromStorage(debugLog).finally(() => {
|
|
497
|
+
pendingResolve = null;
|
|
498
|
+
});
|
|
499
|
+
return pendingResolve;
|
|
500
|
+
}
|
|
501
|
+
async function resolveStoredLoginFromStorage(debugLog) {
|
|
502
|
+
let cfg;
|
|
503
|
+
try {
|
|
504
|
+
cfg = getConfig();
|
|
505
|
+
} catch (error) {
|
|
506
|
+
debugLog("Failed to load config", {
|
|
507
|
+
error: error instanceof Error ? error.message : error
|
|
508
|
+
});
|
|
509
|
+
return void 0;
|
|
510
|
+
}
|
|
511
|
+
const expiresAt = cfg.get("login_expires_at");
|
|
512
|
+
const configJwt = cfg.get("login_jwt");
|
|
513
|
+
const configRefresh = cfg.get("login_refresh_token");
|
|
514
|
+
if (configJwt && configRefresh && typeof expiresAt === "number") {
|
|
515
|
+
debugLog("Loaded credentials from config (legacy format)");
|
|
516
|
+
cachedLogin = {
|
|
517
|
+
jwt: configJwt,
|
|
518
|
+
refreshToken: configRefresh,
|
|
519
|
+
expiresAt,
|
|
520
|
+
storage: "config"
|
|
521
|
+
};
|
|
522
|
+
return cachedLogin;
|
|
523
|
+
}
|
|
524
|
+
if (typeof expiresAt !== "number") {
|
|
525
|
+
debugLog("No stored login credentials found");
|
|
526
|
+
return void 0;
|
|
527
|
+
}
|
|
528
|
+
const keychainData = await getTokensFromKeychain({ debugLog });
|
|
529
|
+
if (!keychainData) {
|
|
530
|
+
debugLog("No tokens found in keychain");
|
|
531
|
+
return void 0;
|
|
532
|
+
}
|
|
533
|
+
debugLog("Loaded credentials from keychain");
|
|
534
|
+
cachedLogin = {
|
|
535
|
+
jwt: keychainData.login_jwt,
|
|
536
|
+
refreshToken: keychainData.login_refresh_token,
|
|
537
|
+
expiresAt,
|
|
538
|
+
storage: "keychain"
|
|
539
|
+
};
|
|
540
|
+
return cachedLogin;
|
|
541
|
+
}
|
|
542
|
+
async function resolveOrRefreshToken(options = {}) {
|
|
543
|
+
const { debug = false } = options;
|
|
544
|
+
const debugLog = createDebugLog(debug);
|
|
545
|
+
const stored = await resolveStoredLogin(debugLog);
|
|
546
|
+
if (!stored) {
|
|
547
|
+
return void 0;
|
|
548
|
+
}
|
|
549
|
+
const { jwt: storedJwt, refreshToken, expiresAt } = stored;
|
|
550
|
+
if (expiresAt > Date.now() + TOKEN_REFRESH_BUFFER_MS) {
|
|
551
|
+
debugLog("Using cached token (still valid)");
|
|
552
|
+
return storedJwt;
|
|
553
|
+
}
|
|
554
|
+
debugLog("Token expired, refreshing...");
|
|
555
|
+
if (pendingRefresh) {
|
|
556
|
+
debugLog("Waiting for existing refresh to complete");
|
|
557
|
+
return pendingRefresh;
|
|
558
|
+
}
|
|
559
|
+
pendingRefresh = refreshJwt(refreshToken, options).finally(() => {
|
|
560
|
+
pendingRefresh = null;
|
|
561
|
+
});
|
|
562
|
+
return await pendingRefresh;
|
|
563
|
+
}
|
|
564
|
+
async function getToken(options = {}) {
|
|
565
|
+
try {
|
|
566
|
+
return await resolveOrRefreshToken(options);
|
|
567
|
+
} catch (error) {
|
|
568
|
+
const message = error instanceof Error ? error.message : "Token refresh failed";
|
|
569
|
+
throw new ZapierAuthenticationError(
|
|
570
|
+
`${message}
|
|
571
|
+
Please run 'login' to authenticate again.`
|
|
572
|
+
);
|
|
573
|
+
}
|
|
574
|
+
}
|
|
575
|
+
async function getLoggedInUser(options = {}) {
|
|
576
|
+
const jwt2 = await getToken(options).catch(() => void 0);
|
|
577
|
+
if (!jwt2) {
|
|
578
|
+
throw new Error(
|
|
579
|
+
"No valid authentication token available. Please login first."
|
|
580
|
+
);
|
|
581
|
+
}
|
|
582
|
+
let decodedJwt = decodeJwtOrThrow(jwt2);
|
|
583
|
+
if (decodedJwt.payload["sub_type"] == "service") {
|
|
584
|
+
decodedJwt = decodeJwtOrThrow(decodedJwt.payload["njwt"]);
|
|
585
|
+
}
|
|
586
|
+
if (typeof decodedJwt.payload["zap:acc"] !== "string") {
|
|
587
|
+
throw new Error("JWT payload does not contain accountId");
|
|
588
|
+
}
|
|
589
|
+
const accountId = parseInt(decodedJwt.payload["zap:acc"], 10);
|
|
590
|
+
if (isNaN(accountId)) {
|
|
591
|
+
throw new Error("JWT accountId is not a number");
|
|
592
|
+
}
|
|
593
|
+
if (decodedJwt.payload["sub_type"] !== "customuser" || typeof decodedJwt.payload["sub"] !== "string") {
|
|
594
|
+
throw new Error("JWT payload does not contain customUserId");
|
|
595
|
+
}
|
|
596
|
+
const customUserId = parseInt(decodedJwt.payload["sub"], 10);
|
|
597
|
+
if (isNaN(customUserId)) {
|
|
598
|
+
throw new Error("JWT customUserId is not a number");
|
|
599
|
+
}
|
|
600
|
+
const email = decodedJwt.payload["zap:uname"];
|
|
601
|
+
if (typeof email !== "string") {
|
|
602
|
+
throw new Error("JWT payload does not contain email");
|
|
603
|
+
}
|
|
604
|
+
return {
|
|
605
|
+
accountId,
|
|
606
|
+
customUserId,
|
|
607
|
+
email
|
|
608
|
+
};
|
|
609
|
+
}
|
|
610
|
+
function getLoginStorageMode() {
|
|
611
|
+
const cfg = getConfig();
|
|
612
|
+
if (typeof cfg.get("login_jwt") === "string") {
|
|
613
|
+
return "config";
|
|
614
|
+
}
|
|
615
|
+
const explicitMode = cfg.get("login_storage_mode");
|
|
616
|
+
if (explicitMode === "keychain" || explicitMode === "config") {
|
|
617
|
+
return explicitMode;
|
|
618
|
+
}
|
|
619
|
+
return "keychain";
|
|
620
|
+
}
|
|
621
|
+
async function logout(options = {}) {
|
|
622
|
+
const { onEvent } = options;
|
|
623
|
+
const mode = getLoginStorageMode();
|
|
624
|
+
cachedLogin = void 0;
|
|
625
|
+
await clearTokensFromKeychain();
|
|
626
|
+
const cfg = getConfig();
|
|
627
|
+
cfg.set("login_storage_mode", mode);
|
|
628
|
+
cfg.delete("login_expires_at");
|
|
629
|
+
cfg.delete("login_jwt");
|
|
630
|
+
cfg.delete("login_refresh_token");
|
|
631
|
+
onEvent?.({
|
|
632
|
+
type: "auth_logout",
|
|
633
|
+
payload: { message: "Logged out successfully", operation: "logout" },
|
|
634
|
+
timestamp: Date.now()
|
|
635
|
+
});
|
|
636
|
+
}
|
|
637
|
+
function getConfigPath() {
|
|
638
|
+
const cfg = getConfig();
|
|
639
|
+
return cfg.path;
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
// src/utils/constants.ts
|
|
59
643
|
var LOGIN_PORTS = [49505, 50575, 52804, 55981, 61010, 63851];
|
|
60
644
|
var LOGIN_TIMEOUT_MS = 3e5;
|
|
61
645
|
var ZapierCliError = class extends zapierSdk.ZapierError {
|
|
@@ -209,13 +793,13 @@ var login = async ({
|
|
|
209
793
|
timeoutMs = LOGIN_TIMEOUT_MS,
|
|
210
794
|
credentials
|
|
211
795
|
}) => {
|
|
212
|
-
const { clientId, tokenUrl, authorizeUrl } =
|
|
796
|
+
const { clientId, tokenUrl, authorizeUrl } = getPkceLoginConfig({
|
|
213
797
|
credentials
|
|
214
798
|
});
|
|
215
799
|
const scope = ensureOfflineAccess(
|
|
216
800
|
credentials?.scope || "internal credentials"
|
|
217
801
|
);
|
|
218
|
-
await
|
|
802
|
+
await logout();
|
|
219
803
|
const availablePort = await findAvailablePort();
|
|
220
804
|
const redirectUri = `http://localhost:${availablePort}/oauth`;
|
|
221
805
|
log_default.info(`Using port ${availablePort} for OAuth callback`);
|
|
@@ -303,13 +887,13 @@ var login = async ({
|
|
|
303
887
|
},
|
|
304
888
|
{
|
|
305
889
|
headers: {
|
|
306
|
-
[
|
|
890
|
+
[AUTH_MODE_HEADER]: "no",
|
|
307
891
|
"Content-Type": "application/x-www-form-urlencoded"
|
|
308
892
|
}
|
|
309
893
|
}
|
|
310
894
|
);
|
|
311
895
|
let targetStorage;
|
|
312
|
-
if (
|
|
896
|
+
if (getLoginStorageMode() === "config") {
|
|
313
897
|
const { upgrade } = await inquirer__default.default.prompt([
|
|
314
898
|
{
|
|
315
899
|
type: "confirm",
|
|
@@ -323,13 +907,13 @@ var login = async ({
|
|
|
323
907
|
targetStorage = "keychain";
|
|
324
908
|
}
|
|
325
909
|
try {
|
|
326
|
-
await
|
|
910
|
+
await updateLogin(data, { storage: targetStorage });
|
|
327
911
|
} catch (err) {
|
|
328
912
|
if (targetStorage === "keychain") {
|
|
329
913
|
log_default.warn(
|
|
330
|
-
`Could not store credentials in system keychain. Storing in plaintext at ${
|
|
914
|
+
`Could not store credentials in system keychain. Storing in plaintext at ${getConfigPath()}.`
|
|
331
915
|
);
|
|
332
|
-
await
|
|
916
|
+
await updateLogin(data, { storage: "config" });
|
|
333
917
|
} else {
|
|
334
918
|
throw err;
|
|
335
919
|
}
|
|
@@ -366,7 +950,7 @@ var loginPlugin = (sdk) => {
|
|
|
366
950
|
timeoutMs: timeoutSeconds * 1e3,
|
|
367
951
|
credentials: pkceCredentials
|
|
368
952
|
});
|
|
369
|
-
const user = await
|
|
953
|
+
const user = await getLoggedInUser();
|
|
370
954
|
sdk.context.eventEmission.emit(
|
|
371
955
|
"platform.sdk.ApplicationLifecycleEvent",
|
|
372
956
|
zapierSdk.buildApplicationLifecycleEvent(
|
|
@@ -393,7 +977,7 @@ var LogoutSchema = zod.z.object({}).describe("Log out of your Zapier account");
|
|
|
393
977
|
|
|
394
978
|
// src/plugins/logout/index.ts
|
|
395
979
|
var logoutWithSdk = zapierSdk.createFunction(async function logoutWithSdk2(_options) {
|
|
396
|
-
await
|
|
980
|
+
await logout();
|
|
397
981
|
console.log("\u2705 Successfully logged out");
|
|
398
982
|
}, LogoutSchema);
|
|
399
983
|
var logoutPlugin = () => ({
|
|
@@ -528,7 +1112,7 @@ var GetLoginConfigPathSchema = zod.z.object({}).describe("Show the path to the l
|
|
|
528
1112
|
var getLoginConfigPathPlugin = () => {
|
|
529
1113
|
const getLoginConfigPathWithSdk = zapierSdk.createFunction(
|
|
530
1114
|
async function getLoginConfigPathWithSdk2(_options) {
|
|
531
|
-
return
|
|
1115
|
+
return getConfigPath();
|
|
532
1116
|
},
|
|
533
1117
|
GetLoginConfigPathSchema
|
|
534
1118
|
);
|
|
@@ -1557,7 +2141,7 @@ async function postWithRetry({
|
|
|
1557
2141
|
var feedbackPlugin = (sdk) => {
|
|
1558
2142
|
const debug = sdk.context.options?.debug;
|
|
1559
2143
|
const feedbackWithSdk = zapierSdk.createFunction(async function feedback(options) {
|
|
1560
|
-
const user = await
|
|
2144
|
+
const user = await getLoggedInUser();
|
|
1561
2145
|
const body = JSON.stringify({
|
|
1562
2146
|
email: user.email,
|
|
1563
2147
|
customuser_id: user.customUserId,
|
|
@@ -2502,10 +3086,10 @@ var initPlugin = () => {
|
|
|
2502
3086
|
// package.json with { type: 'json' }
|
|
2503
3087
|
var package_default = {
|
|
2504
3088
|
name: "@zapier/zapier-sdk-cli",
|
|
2505
|
-
version: "0.
|
|
3089
|
+
version: "0.43.0"};
|
|
2506
3090
|
|
|
2507
3091
|
// src/sdk.ts
|
|
2508
|
-
zapierSdk.injectCliLogin(
|
|
3092
|
+
zapierSdk.injectCliLogin(login_exports);
|
|
2509
3093
|
function createZapierCliSdk(options = {}) {
|
|
2510
3094
|
return zapierSdk.createZapierSdk({
|
|
2511
3095
|
...options,
|
|
@@ -2516,7 +3100,7 @@ function createZapierCliSdk(options = {}) {
|
|
|
2516
3100
|
|
|
2517
3101
|
// package.json
|
|
2518
3102
|
var package_default2 = {
|
|
2519
|
-
version: "0.
|
|
3103
|
+
version: "0.43.0"};
|
|
2520
3104
|
|
|
2521
3105
|
// src/telemetry/builders.ts
|
|
2522
3106
|
function createCliBaseEvent(context = {}) {
|