@zapier/zapier-sdk-cli 0.60.0 → 0.61.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/CHANGELOG.md +16 -0
- package/README.md +2 -1
- package/dist/cli.cjs +514 -201
- package/dist/cli.mjs +515 -202
- package/dist/experimental.cjs +329 -195
- package/dist/experimental.d.mts +3 -3
- package/dist/experimental.d.ts +3 -3
- package/dist/experimental.mjs +329 -195
- package/dist/{sdk-SOLizjno.d.mts → extensions-DXyB9Vsr.d.mts} +11 -2
- package/dist/{sdk-SOLizjno.d.ts → extensions-DXyB9Vsr.d.ts} +11 -2
- package/dist/index.cjs +330 -196
- package/dist/index.d.mts +3 -3
- package/dist/index.d.ts +3 -3
- package/dist/index.mjs +330 -196
- package/package.json +3 -3
package/dist/cli.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { Command, CommanderError, Option } from 'commander';
|
|
3
3
|
import { z } from 'zod';
|
|
4
|
-
import { definePlugin, createPluginMethod, OutputPropertySchema, ZapierBundleError, DEFAULT_CONFIG_PATH, ZapierValidationError, ZapierUnknownError, ZapierReleaseTriggerMessageSignal, injectCliLogin, BaseSdkOptionsSchema, getOrCreateApiClient, isPermanentHttpError, invalidateCachedToken, batch, toSnakeCase, ZapierAbortDrainSignal, createZapierSdkStack as createZapierSdkStack$1, createSdk as createSdk$1, defineLegacyMerge as defineLegacyMerge$1, zapierSdkPlugin as zapierSdkPlugin$1, addPlugin as addPlugin$1, ZapierError, isCredentialsObject, buildApplicationLifecycleEvent, AuthMechanism, ZapierAuthenticationError, DEPRECATION_NOTICE_EVENT, runWithCallerContext, isPositional, runWithTelemetryContext, buildCapabilityMessage, formatErrorMessage, getOsInfo, getPlatformVersions, getAgent, getTtyContext, getCiPlatform, isCi, getReleaseId, getCurrentTimestamp, generateEventId, ZapierApprovalError } from '@zapier/zapier-sdk';
|
|
4
|
+
import { definePlugin, createPluginMethod, OutputPropertySchema, ZapierBundleError, DEFAULT_CONFIG_PATH, ZapierValidationError, ZapierUnknownError, ZapierReleaseTriggerMessageSignal, injectCliLogin, BaseSdkOptionsSchema, getOrCreateApiClient, isPermanentHttpError, invalidateCachedToken, batch, toSnakeCase, ZapierAbortDrainSignal, createZapierSdkStack as createZapierSdkStack$1, createSdk as createSdk$1, defineLegacyMerge as defineLegacyMerge$1, zapierSdkPlugin as zapierSdkPlugin$1, addPlugin as addPlugin$1, ZapierError, isCredentialsObject, buildApplicationLifecycleEvent, AuthMechanism, ZapierAuthenticationError, DEPRECATION_NOTICE_EVENT, runWithCallerContext, isPositional, createController, CoreCancelledSignal, runWithTelemetryContext, buildCapabilityMessage, formatErrorMessage, getOsInfo, getPlatformVersions, getAgent, getTtyContext, getCiPlatform, isCi, getReleaseId, getCurrentTimestamp, generateEventId, ZapierApprovalError } from '@zapier/zapier-sdk';
|
|
5
5
|
import inquirer from 'inquirer';
|
|
6
6
|
import search from '@inquirer/search';
|
|
7
7
|
import chalk from 'chalk';
|
|
@@ -59,12 +59,18 @@ var ZapierCliExitError = class extends ZapierCliError {
|
|
|
59
59
|
this.exitCode = exitCode;
|
|
60
60
|
}
|
|
61
61
|
};
|
|
62
|
-
var ZapierCliValidationError = class extends ZapierCliError {
|
|
63
|
-
constructor(message) {
|
|
62
|
+
var ZapierCliValidationError = class _ZapierCliValidationError extends ZapierCliError {
|
|
63
|
+
constructor(message, options = {}) {
|
|
64
64
|
super(message);
|
|
65
|
-
this.name = "ZapierCliValidationError";
|
|
66
|
-
this.code = "ZAPIER_CLI_VALIDATION_ERROR";
|
|
67
65
|
this.exitCode = 1;
|
|
66
|
+
this.name = options.name ?? "ZapierCliValidationError";
|
|
67
|
+
this.code = options.code ?? "ZAPIER_CLI_VALIDATION_ERROR";
|
|
68
|
+
}
|
|
69
|
+
withMessage(message) {
|
|
70
|
+
return new _ZapierCliValidationError(message, {
|
|
71
|
+
name: this.name,
|
|
72
|
+
code: this.code
|
|
73
|
+
});
|
|
68
74
|
}
|
|
69
75
|
};
|
|
70
76
|
var ZapierCliMissingParametersError = class extends ZapierCliError {
|
|
@@ -1505,6 +1511,148 @@ Optional fields${pathContext}:`));
|
|
|
1505
1511
|
return constants;
|
|
1506
1512
|
}
|
|
1507
1513
|
};
|
|
1514
|
+
function offers(question, action) {
|
|
1515
|
+
return question.actions.some((a) => a.action === action);
|
|
1516
|
+
}
|
|
1517
|
+
function isActionRow(value) {
|
|
1518
|
+
return typeof value === "object" && value !== null && "action" in value;
|
|
1519
|
+
}
|
|
1520
|
+
async function promptText({
|
|
1521
|
+
message,
|
|
1522
|
+
password
|
|
1523
|
+
}) {
|
|
1524
|
+
const { value } = await inquirer.prompt([
|
|
1525
|
+
{ type: password ? "password" : "input", name: "value", message }
|
|
1526
|
+
]);
|
|
1527
|
+
return value;
|
|
1528
|
+
}
|
|
1529
|
+
async function answerSelect(question, field) {
|
|
1530
|
+
const display = (c) => c.hint ? `${c.label} ${chalk.dim(`(${c.hint})`)}` : c.label;
|
|
1531
|
+
if (question.multiple) {
|
|
1532
|
+
const choices = [
|
|
1533
|
+
...question.choices.map((c) => ({ name: display(c), value: c.value })),
|
|
1534
|
+
...offers(question, "more") ? [
|
|
1535
|
+
{
|
|
1536
|
+
name: chalk.dim("Load more\u2026"),
|
|
1537
|
+
value: { action: "more" }
|
|
1538
|
+
}
|
|
1539
|
+
] : [],
|
|
1540
|
+
...(question.notes ?? []).map((note) => ({
|
|
1541
|
+
name: chalk.dim(note),
|
|
1542
|
+
value: note,
|
|
1543
|
+
disabled: true
|
|
1544
|
+
}))
|
|
1545
|
+
];
|
|
1546
|
+
const { values } = await inquirer.prompt([
|
|
1547
|
+
{ type: "checkbox", name: "values", message: question.message, choices }
|
|
1548
|
+
]);
|
|
1549
|
+
const selected = values;
|
|
1550
|
+
if (selected.some(isActionRow)) return { type: "more" };
|
|
1551
|
+
if (selected.length === 0 && offers(question, "skip")) {
|
|
1552
|
+
return { type: "skip" };
|
|
1553
|
+
}
|
|
1554
|
+
return { type: "choose", value: selected };
|
|
1555
|
+
}
|
|
1556
|
+
const row = (name, action) => ({
|
|
1557
|
+
name,
|
|
1558
|
+
value: { action }
|
|
1559
|
+
});
|
|
1560
|
+
const value = await search({
|
|
1561
|
+
message: question.message,
|
|
1562
|
+
source: (term) => {
|
|
1563
|
+
const t = (term ?? "").trim().toLowerCase();
|
|
1564
|
+
const matched = t ? question.choices.filter(
|
|
1565
|
+
(c) => `${c.label} ${c.hint ?? ""}`.toLowerCase().includes(t)
|
|
1566
|
+
) : question.choices;
|
|
1567
|
+
const rows = matched.map((c) => ({
|
|
1568
|
+
name: display(c),
|
|
1569
|
+
value: c.value
|
|
1570
|
+
}));
|
|
1571
|
+
if (offers(question, "search"))
|
|
1572
|
+
rows.push(row(chalk.cyan("Search\u2026"), "search"));
|
|
1573
|
+
if (offers(question, "more"))
|
|
1574
|
+
rows.push(row(chalk.dim("Load more\u2026"), "more"));
|
|
1575
|
+
if (offers(question, "custom"))
|
|
1576
|
+
rows.push(row(chalk.dim("Enter a value manually\u2026"), "custom"));
|
|
1577
|
+
if (offers(question, "retry"))
|
|
1578
|
+
rows.push(row(chalk.yellow("Retry"), "retry"));
|
|
1579
|
+
if (offers(question, "skip"))
|
|
1580
|
+
rows.push(row(chalk.dim("Skip (optional)"), "skip"));
|
|
1581
|
+
if (offers(question, "cancel"))
|
|
1582
|
+
rows.push(row(chalk.dim("Cancel"), "cancel"));
|
|
1583
|
+
for (const note of question.notes ?? [])
|
|
1584
|
+
rows.push({ name: chalk.dim(note), value: note, disabled: true });
|
|
1585
|
+
return rows;
|
|
1586
|
+
}
|
|
1587
|
+
});
|
|
1588
|
+
if (!isActionRow(value)) {
|
|
1589
|
+
return { type: "choose", value };
|
|
1590
|
+
}
|
|
1591
|
+
switch (value.action) {
|
|
1592
|
+
case "search": {
|
|
1593
|
+
const term = await promptText({
|
|
1594
|
+
message: `Search ${field}:`,
|
|
1595
|
+
password: false
|
|
1596
|
+
});
|
|
1597
|
+
return { type: "search", term };
|
|
1598
|
+
}
|
|
1599
|
+
case "custom": {
|
|
1600
|
+
const custom = await promptText({
|
|
1601
|
+
message: `Enter ${field}:`,
|
|
1602
|
+
password: false
|
|
1603
|
+
});
|
|
1604
|
+
return { type: "custom", value: custom };
|
|
1605
|
+
}
|
|
1606
|
+
case "more":
|
|
1607
|
+
return { type: "more" };
|
|
1608
|
+
case "retry":
|
|
1609
|
+
return { type: "retry" };
|
|
1610
|
+
case "skip":
|
|
1611
|
+
return { type: "skip" };
|
|
1612
|
+
case "cancel":
|
|
1613
|
+
return { type: "cancel" };
|
|
1614
|
+
}
|
|
1615
|
+
}
|
|
1616
|
+
async function answerInput(question) {
|
|
1617
|
+
const value = await promptText({
|
|
1618
|
+
message: question.message,
|
|
1619
|
+
password: question.inputType === "password"
|
|
1620
|
+
});
|
|
1621
|
+
if (value === "" && offers(question, "skip")) {
|
|
1622
|
+
return { type: "skip" };
|
|
1623
|
+
}
|
|
1624
|
+
return { type: "custom", value };
|
|
1625
|
+
}
|
|
1626
|
+
async function answerCollection(question) {
|
|
1627
|
+
if (!offers(question, "done")) {
|
|
1628
|
+
return { type: "add" };
|
|
1629
|
+
}
|
|
1630
|
+
const { again } = await inquirer.prompt([
|
|
1631
|
+
{
|
|
1632
|
+
type: "confirm",
|
|
1633
|
+
name: "again",
|
|
1634
|
+
message: question.message,
|
|
1635
|
+
default: false
|
|
1636
|
+
}
|
|
1637
|
+
]);
|
|
1638
|
+
return again ? { type: "add" } : { type: "done" };
|
|
1639
|
+
}
|
|
1640
|
+
var answerViaCli = ({ state, result }) => {
|
|
1641
|
+
if (result.error !== void 0) {
|
|
1642
|
+
const message = typeof result.error === "string" ? result.error : result.error.message;
|
|
1643
|
+
console.log(chalk.yellow(`! ${message}`));
|
|
1644
|
+
}
|
|
1645
|
+
const field = state.current?.join(".") ?? "value";
|
|
1646
|
+
const question = result.question;
|
|
1647
|
+
switch (question.type) {
|
|
1648
|
+
case "select":
|
|
1649
|
+
return answerSelect(question, field);
|
|
1650
|
+
case "input":
|
|
1651
|
+
return answerInput(question);
|
|
1652
|
+
case "collection":
|
|
1653
|
+
return answerCollection(question);
|
|
1654
|
+
}
|
|
1655
|
+
};
|
|
1508
1656
|
|
|
1509
1657
|
// src/utils/cli-options.ts
|
|
1510
1658
|
var RESERVED_CLI_OPTIONS = [
|
|
@@ -1536,7 +1684,7 @@ var SHARED_COMMAND_CLI_OPTIONS = [
|
|
|
1536
1684
|
|
|
1537
1685
|
// package.json
|
|
1538
1686
|
var package_default = {
|
|
1539
|
-
version: "0.
|
|
1687
|
+
version: "0.61.1"};
|
|
1540
1688
|
|
|
1541
1689
|
// src/telemetry/builders.ts
|
|
1542
1690
|
function createCliBaseEvent(context = {}) {
|
|
@@ -1611,7 +1759,7 @@ async function formatItemsFromSchema(_functionInfo, items, startingNumber = 0, o
|
|
|
1611
1759
|
if (options?.formatter) {
|
|
1612
1760
|
const formatter = options.formatter;
|
|
1613
1761
|
const input = options.input ?? {};
|
|
1614
|
-
const context = formatter.
|
|
1762
|
+
const context = formatter.getContext ? await formatter.getContext({ items, input }) : void 0;
|
|
1615
1763
|
items.forEach((item, index) => {
|
|
1616
1764
|
const formatted = formatter.format({ item, input, context });
|
|
1617
1765
|
formatSingleItem(formatted, startingNumber + index);
|
|
@@ -2206,7 +2354,7 @@ function analyzeZodField(name, schema, functionInfo) {
|
|
|
2206
2354
|
paramType = "object";
|
|
2207
2355
|
}
|
|
2208
2356
|
let paramHasResolver = false;
|
|
2209
|
-
if (functionInfo?.resolvers?.[name]) {
|
|
2357
|
+
if (functionInfo?.resolvers?.[name] || functionInfo?.boundResolvers?.[name]) {
|
|
2210
2358
|
paramHasResolver = true;
|
|
2211
2359
|
}
|
|
2212
2360
|
return {
|
|
@@ -2365,6 +2513,9 @@ function createCommandConfig(cliCommandName, functionInfo, sdk) {
|
|
|
2365
2513
|
const usesInputParameters = !functionInfo.inputSchema && !!functionInfo.inputParameters;
|
|
2366
2514
|
const schema = functionInfo.inputSchema;
|
|
2367
2515
|
const parameters = usesInputParameters ? analyzeInputParameters(functionInfo.inputParameters, functionInfo) : analyzeZodSchema(schema, functionInfo);
|
|
2516
|
+
if (functionInfo.boundResolvers && Object.keys(functionInfo.boundResolvers).length > 0) {
|
|
2517
|
+
for (const param of parameters) param.hasResolver = true;
|
|
2518
|
+
}
|
|
2368
2519
|
const schemaAliases = getSchemaAliases(schema);
|
|
2369
2520
|
if (schemaAliases) {
|
|
2370
2521
|
const aliasedNames = new Set(Object.keys(schemaAliases));
|
|
@@ -2417,7 +2568,35 @@ function createCommandConfig(cliCommandName, functionInfo, sdk) {
|
|
|
2417
2568
|
}
|
|
2418
2569
|
}
|
|
2419
2570
|
}
|
|
2420
|
-
|
|
2571
|
+
const boundResolvers = functionInfo.boundResolvers;
|
|
2572
|
+
if (boundResolvers && Object.keys(boundResolvers).length > 0) {
|
|
2573
|
+
const seedInput = Object.fromEntries(
|
|
2574
|
+
Object.entries(rawParams).filter(([, v]) => v !== void 0)
|
|
2575
|
+
);
|
|
2576
|
+
const controller = createController(sdk);
|
|
2577
|
+
let resolved;
|
|
2578
|
+
try {
|
|
2579
|
+
resolved = await controller.resolve({
|
|
2580
|
+
method: functionInfo.name,
|
|
2581
|
+
input: seedInput,
|
|
2582
|
+
answer: interactiveMode ? answerViaCli : ({ state }) => {
|
|
2583
|
+
throw new ZapierCliMissingParametersError([
|
|
2584
|
+
{
|
|
2585
|
+
name: state.current?.join(".") ?? "value",
|
|
2586
|
+
isPositional: false
|
|
2587
|
+
}
|
|
2588
|
+
]);
|
|
2589
|
+
},
|
|
2590
|
+
interactive: interactiveMode
|
|
2591
|
+
});
|
|
2592
|
+
} catch (err) {
|
|
2593
|
+
if (err instanceof CoreCancelledSignal) {
|
|
2594
|
+
throw new ZapierCliUserCancellationError();
|
|
2595
|
+
}
|
|
2596
|
+
throw err;
|
|
2597
|
+
}
|
|
2598
|
+
Object.assign(resolvedParams, resolved);
|
|
2599
|
+
} else if (schema && !usesInputParameters) {
|
|
2421
2600
|
const resolver = new SchemaParameterResolver();
|
|
2422
2601
|
const resolved = await resolver.resolveParameters(
|
|
2423
2602
|
schema,
|
|
@@ -2510,7 +2689,7 @@ ${confirmMessageAfter}`));
|
|
|
2510
2689
|
}
|
|
2511
2690
|
}
|
|
2512
2691
|
} catch (error) {
|
|
2513
|
-
success = false;
|
|
2692
|
+
success = error instanceof ZapierCliError ? error.exitCode === 0 : false;
|
|
2514
2693
|
errorMessage = error instanceof Error ? error.message : String(error);
|
|
2515
2694
|
if (error instanceof ZapierCliMissingParametersError) {
|
|
2516
2695
|
renderer.renderError(error);
|
|
@@ -3722,41 +3901,165 @@ var spinPromise = async (promise, text) => {
|
|
|
3722
3901
|
}
|
|
3723
3902
|
};
|
|
3724
3903
|
|
|
3904
|
+
// src/utils/auth/oauth-errors.ts
|
|
3905
|
+
var OauthFlowTimeoutError = class _OauthFlowTimeoutError extends ZapierCliValidationError {
|
|
3906
|
+
constructor({
|
|
3907
|
+
timeoutMs,
|
|
3908
|
+
message = "OAuth flow timed out"
|
|
3909
|
+
}) {
|
|
3910
|
+
super(message, {
|
|
3911
|
+
name: "OauthFlowTimeoutError",
|
|
3912
|
+
code: "ZAPIER_OAUTH_FLOW_TIMEOUT"
|
|
3913
|
+
});
|
|
3914
|
+
this.timeoutMs = timeoutMs;
|
|
3915
|
+
}
|
|
3916
|
+
withMessage(message) {
|
|
3917
|
+
return new _OauthFlowTimeoutError({ timeoutMs: this.timeoutMs, message });
|
|
3918
|
+
}
|
|
3919
|
+
};
|
|
3920
|
+
var OauthAuthorizationDeniedError = class _OauthAuthorizationDeniedError extends ZapierCliValidationError {
|
|
3921
|
+
constructor({
|
|
3922
|
+
reason,
|
|
3923
|
+
message = "OAuth authorization denied"
|
|
3924
|
+
}) {
|
|
3925
|
+
super(message, {
|
|
3926
|
+
name: "OauthAuthorizationDeniedError",
|
|
3927
|
+
code: "ZAPIER_OAUTH_AUTHORIZATION_DENIED"
|
|
3928
|
+
});
|
|
3929
|
+
this.reason = reason;
|
|
3930
|
+
}
|
|
3931
|
+
withMessage(message) {
|
|
3932
|
+
return new _OauthAuthorizationDeniedError({
|
|
3933
|
+
reason: this.reason,
|
|
3934
|
+
message
|
|
3935
|
+
});
|
|
3936
|
+
}
|
|
3937
|
+
};
|
|
3938
|
+
var OauthFlowError = class _OauthFlowError extends ZapierCliValidationError {
|
|
3939
|
+
constructor({ message }) {
|
|
3940
|
+
super(message, {
|
|
3941
|
+
name: "OauthFlowError",
|
|
3942
|
+
code: "ZAPIER_OAUTH_FLOW"
|
|
3943
|
+
});
|
|
3944
|
+
}
|
|
3945
|
+
withMessage(message) {
|
|
3946
|
+
return new _OauthFlowError({ message });
|
|
3947
|
+
}
|
|
3948
|
+
};
|
|
3949
|
+
var OauthCallbackError = class _OauthCallbackError extends ZapierCliValidationError {
|
|
3950
|
+
constructor({ kind, message }) {
|
|
3951
|
+
super(message, {
|
|
3952
|
+
name: "OauthCallbackError",
|
|
3953
|
+
code: "ZAPIER_OAUTH_CALLBACK"
|
|
3954
|
+
});
|
|
3955
|
+
this.kind = kind;
|
|
3956
|
+
}
|
|
3957
|
+
withMessage(message) {
|
|
3958
|
+
return new _OauthCallbackError({ kind: this.kind, message });
|
|
3959
|
+
}
|
|
3960
|
+
};
|
|
3961
|
+
var OauthTokenExchangeError = class _OauthTokenExchangeError extends ZapierCliValidationError {
|
|
3962
|
+
constructor({ message }) {
|
|
3963
|
+
super(message, {
|
|
3964
|
+
name: "OauthTokenExchangeError",
|
|
3965
|
+
code: "ZAPIER_OAUTH_TOKEN_EXCHANGE"
|
|
3966
|
+
});
|
|
3967
|
+
}
|
|
3968
|
+
withMessage(message) {
|
|
3969
|
+
return new _OauthTokenExchangeError({ message });
|
|
3970
|
+
}
|
|
3971
|
+
};
|
|
3972
|
+
var SENSITIVE_OAUTH_FIELDS = [
|
|
3973
|
+
"access_token",
|
|
3974
|
+
"refresh_token",
|
|
3975
|
+
"id_token",
|
|
3976
|
+
"client_secret",
|
|
3977
|
+
"code_verifier",
|
|
3978
|
+
"code_challenge"
|
|
3979
|
+
];
|
|
3980
|
+
function getErrorMessage(error) {
|
|
3981
|
+
return error instanceof Error ? error.message : String(error);
|
|
3982
|
+
}
|
|
3983
|
+
function toCamelCase(field) {
|
|
3984
|
+
return field.replace(
|
|
3985
|
+
/_([a-z])/g,
|
|
3986
|
+
(_match, letter) => letter.toUpperCase()
|
|
3987
|
+
);
|
|
3988
|
+
}
|
|
3989
|
+
function escapeRegExp(value) {
|
|
3990
|
+
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
3991
|
+
}
|
|
3992
|
+
var sensitiveOauthFieldPattern = Array.from(
|
|
3993
|
+
new Set(
|
|
3994
|
+
SENSITIVE_OAUTH_FIELDS.flatMap((field) => [field, toCamelCase(field)])
|
|
3995
|
+
)
|
|
3996
|
+
).map(escapeRegExp).join("|");
|
|
3997
|
+
var sensitiveQueryParamPattern = new RegExp(
|
|
3998
|
+
`([?&])(${sensitiveOauthFieldPattern})(=)[^&#\\s"'<>]*`,
|
|
3999
|
+
"gi"
|
|
4000
|
+
);
|
|
4001
|
+
function redactSensitiveOauthErrorMessage(message) {
|
|
4002
|
+
return message.replace(
|
|
4003
|
+
sensitiveQueryParamPattern,
|
|
4004
|
+
(_match, prefix, key, separator) => `${prefix}${key}${separator}[REDACTED]`
|
|
4005
|
+
).replace(
|
|
4006
|
+
new RegExp(`"(${sensitiveOauthFieldPattern})"(\\s*:\\s*)"[^"]*"`, "g"),
|
|
4007
|
+
(_match, key, separator) => `"${key}"${separator}"[REDACTED]"`
|
|
4008
|
+
);
|
|
4009
|
+
}
|
|
4010
|
+
function toRedactedOauthError(error) {
|
|
4011
|
+
const message = redactSensitiveOauthErrorMessage(getErrorMessage(error));
|
|
4012
|
+
if (error instanceof ZapierCliValidationError) {
|
|
4013
|
+
return error.withMessage(message);
|
|
4014
|
+
}
|
|
4015
|
+
return new OauthFlowError({ message });
|
|
4016
|
+
}
|
|
4017
|
+
function toOauthTokenExchangeError(error) {
|
|
4018
|
+
return new OauthTokenExchangeError({
|
|
4019
|
+
message: redactSensitiveOauthErrorMessage(getErrorMessage(error))
|
|
4020
|
+
});
|
|
4021
|
+
}
|
|
4022
|
+
|
|
3725
4023
|
// src/utils/auth/oauth-callback.ts
|
|
3726
4024
|
function getCallbackCode({
|
|
3727
4025
|
callbackUrl,
|
|
3728
|
-
transaction
|
|
3729
|
-
recoveryMessage
|
|
4026
|
+
transaction
|
|
3730
4027
|
}) {
|
|
3731
4028
|
let parsed;
|
|
3732
4029
|
try {
|
|
3733
4030
|
parsed = new URL(callbackUrl.trim());
|
|
3734
4031
|
} catch {
|
|
3735
|
-
throw new
|
|
3736
|
-
|
|
3737
|
-
|
|
4032
|
+
throw new OauthCallbackError({
|
|
4033
|
+
kind: "invalid_url",
|
|
4034
|
+
message: "Paste the final OAuth callback URL from your browser."
|
|
4035
|
+
});
|
|
3738
4036
|
}
|
|
3739
4037
|
const expected = new URL(transaction.redirectUri);
|
|
3740
4038
|
if (parsed.protocol !== "http:" || parsed.hostname !== expected.hostname || parsed.pathname !== expected.pathname || parsed.port !== expected.port) {
|
|
3741
|
-
throw new
|
|
3742
|
-
|
|
3743
|
-
|
|
4039
|
+
throw new OauthCallbackError({
|
|
4040
|
+
kind: "redirect_mismatch",
|
|
4041
|
+
message: `Expected the final OAuth callback URL to start with ${transaction.redirectUri}.`
|
|
4042
|
+
});
|
|
3744
4043
|
}
|
|
3745
4044
|
if (parsed.searchParams.get("state") !== transaction.state) {
|
|
3746
|
-
throw new
|
|
3747
|
-
|
|
3748
|
-
|
|
4045
|
+
throw new OauthCallbackError({
|
|
4046
|
+
kind: "state_mismatch",
|
|
4047
|
+
message: "OAuth state mismatch."
|
|
4048
|
+
});
|
|
3749
4049
|
}
|
|
3750
4050
|
if (parsed.searchParams.has("error")) {
|
|
3751
|
-
throw new
|
|
3752
|
-
|
|
3753
|
-
|
|
4051
|
+
throw new OauthAuthorizationDeniedError({
|
|
4052
|
+
reason: String(
|
|
4053
|
+
parsed.searchParams.get("error_description") ?? parsed.searchParams.get("error")
|
|
4054
|
+
)
|
|
4055
|
+
});
|
|
3754
4056
|
}
|
|
3755
4057
|
const code = parsed.searchParams.get("code");
|
|
3756
4058
|
if (!code) {
|
|
3757
|
-
throw new
|
|
3758
|
-
|
|
3759
|
-
|
|
4059
|
+
throw new OauthCallbackError({
|
|
4060
|
+
kind: "missing_code",
|
|
4061
|
+
message: "No authorization code found in the pasted callback URL."
|
|
4062
|
+
});
|
|
3760
4063
|
}
|
|
3761
4064
|
return code;
|
|
3762
4065
|
}
|
|
@@ -3871,7 +4174,9 @@ async function exchangeOauthCode({
|
|
|
3871
4174
|
"Content-Type": "application/x-www-form-urlencoded"
|
|
3872
4175
|
}
|
|
3873
4176
|
}
|
|
3874
|
-
)
|
|
4177
|
+
).catch((error) => {
|
|
4178
|
+
throw toOauthTokenExchangeError(error);
|
|
4179
|
+
});
|
|
3875
4180
|
return {
|
|
3876
4181
|
accessToken: data.access_token,
|
|
3877
4182
|
refreshToken: data.refresh_token,
|
|
@@ -3880,19 +4185,15 @@ async function exchangeOauthCode({
|
|
|
3880
4185
|
}
|
|
3881
4186
|
|
|
3882
4187
|
// src/utils/auth/oauth-flow.ts
|
|
3883
|
-
var
|
|
3884
|
-
|
|
3885
|
-
|
|
3886
|
-
|
|
3887
|
-
this.name = "OauthFlowTimeoutError";
|
|
3888
|
-
}
|
|
4188
|
+
var LOGIN_OAUTH_COPY = {
|
|
4189
|
+
flowName: "Login",
|
|
4190
|
+
action: "log in",
|
|
4191
|
+
urlLabel: "login URL"
|
|
3889
4192
|
};
|
|
3890
|
-
var
|
|
3891
|
-
|
|
3892
|
-
|
|
3893
|
-
|
|
3894
|
-
this.name = "OauthAuthorizationDeniedError";
|
|
3895
|
-
}
|
|
4193
|
+
var SIGNUP_OAUTH_COPY = {
|
|
4194
|
+
flowName: "Signup",
|
|
4195
|
+
action: "sign up",
|
|
4196
|
+
urlLabel: "signup URL"
|
|
3896
4197
|
};
|
|
3897
4198
|
function findAvailablePort() {
|
|
3898
4199
|
return new Promise((resolve4, reject) => {
|
|
@@ -3907,70 +4208,86 @@ function findAvailablePort() {
|
|
|
3907
4208
|
tryPort(LOGIN_PORTS[portIndex++]);
|
|
3908
4209
|
} else if (err.code === "EADDRINUSE") {
|
|
3909
4210
|
reject(
|
|
3910
|
-
new
|
|
3911
|
-
`All configured OAuth callback ports are busy: ${LOGIN_PORTS.join(", ")}. Please try again later or close applications using these ports.`
|
|
3912
|
-
)
|
|
4211
|
+
new OauthFlowError({
|
|
4212
|
+
message: `All configured OAuth callback ports are busy: ${LOGIN_PORTS.join(", ")}. Please try again later or close applications using these ports.`
|
|
4213
|
+
})
|
|
3913
4214
|
);
|
|
3914
4215
|
} else {
|
|
3915
4216
|
reject(err);
|
|
3916
4217
|
}
|
|
3917
4218
|
});
|
|
3918
4219
|
};
|
|
3919
|
-
if (LOGIN_PORTS.length > 0)
|
|
3920
|
-
|
|
4220
|
+
if (LOGIN_PORTS.length > 0) {
|
|
4221
|
+
tryPort(LOGIN_PORTS[portIndex++]);
|
|
4222
|
+
return;
|
|
4223
|
+
}
|
|
4224
|
+
reject(
|
|
4225
|
+
new OauthFlowError({ message: "No OAuth callback ports configured" })
|
|
4226
|
+
);
|
|
3921
4227
|
});
|
|
3922
4228
|
}
|
|
3923
4229
|
async function runLoginOauthFlow(options) {
|
|
3924
|
-
return
|
|
3925
|
-
|
|
4230
|
+
return runOauthFlowForEntryPoint({
|
|
4231
|
+
options,
|
|
3926
4232
|
entryPoint: "login",
|
|
3927
|
-
|
|
3928
|
-
flowName: "Login"
|
|
4233
|
+
copy: LOGIN_OAUTH_COPY
|
|
3929
4234
|
});
|
|
3930
4235
|
}
|
|
3931
4236
|
async function runSignupOauthFlow(options) {
|
|
4237
|
+
return runOauthFlowForEntryPoint({
|
|
4238
|
+
options,
|
|
4239
|
+
entryPoint: "signup",
|
|
4240
|
+
copy: SIGNUP_OAUTH_COPY
|
|
4241
|
+
});
|
|
4242
|
+
}
|
|
4243
|
+
function runOauthFlowForEntryPoint({
|
|
4244
|
+
options,
|
|
4245
|
+
entryPoint,
|
|
4246
|
+
copy
|
|
4247
|
+
}) {
|
|
3932
4248
|
if (options.headless) {
|
|
3933
4249
|
return runOauthFlowEntryPoint({
|
|
3934
4250
|
...options,
|
|
3935
|
-
entryPoint
|
|
3936
|
-
|
|
3937
|
-
|
|
3938
|
-
headless: true
|
|
4251
|
+
entryPoint,
|
|
4252
|
+
headless: true,
|
|
4253
|
+
copy
|
|
3939
4254
|
});
|
|
3940
4255
|
}
|
|
3941
4256
|
return runOauthFlowEntryPoint({
|
|
3942
4257
|
...options,
|
|
3943
|
-
entryPoint
|
|
3944
|
-
|
|
3945
|
-
|
|
4258
|
+
entryPoint,
|
|
4259
|
+
copy,
|
|
4260
|
+
headless: false
|
|
3946
4261
|
});
|
|
3947
4262
|
}
|
|
3948
|
-
async function runOauthFlowEntryPoint({
|
|
3949
|
-
flowName,
|
|
3950
|
-
...options
|
|
3951
|
-
}) {
|
|
4263
|
+
async function runOauthFlowEntryPoint(options) {
|
|
3952
4264
|
try {
|
|
3953
|
-
return options.headless ? await
|
|
4265
|
+
return options.headless ? await runHeadlessOauthFlow(options) : await runOauthFlow(options);
|
|
3954
4266
|
} catch (error) {
|
|
3955
4267
|
if (error instanceof OauthFlowTimeoutError) {
|
|
3956
|
-
throw
|
|
4268
|
+
throw error.withMessage(
|
|
3957
4269
|
withRecoveryMessage(
|
|
3958
|
-
`${flowName} timed out after ${Math.round(error.timeoutMs / 1e3)} seconds.`,
|
|
4270
|
+
`${options.copy.flowName} timed out after ${Math.round(error.timeoutMs / 1e3)} seconds.`,
|
|
3959
4271
|
options.recoveryMessage
|
|
3960
4272
|
)
|
|
3961
4273
|
);
|
|
3962
4274
|
}
|
|
3963
4275
|
if (error instanceof OauthAuthorizationDeniedError) {
|
|
3964
|
-
throw
|
|
4276
|
+
throw error.withMessage(
|
|
3965
4277
|
withRecoveryMessage(
|
|
3966
4278
|
`Authorization denied: ${error.reason}.`,
|
|
3967
4279
|
options.recoveryMessage
|
|
3968
4280
|
)
|
|
3969
4281
|
);
|
|
3970
4282
|
}
|
|
4283
|
+
if (error instanceof OauthCallbackError && options.recoveryMessage) {
|
|
4284
|
+
throw error.withMessage(
|
|
4285
|
+
withRecoveryMessage(error.message, options.recoveryMessage)
|
|
4286
|
+
);
|
|
4287
|
+
}
|
|
3971
4288
|
if (error instanceof ZapierCliUserCancellationError && !options.silent) {
|
|
3972
4289
|
log_default.info(`
|
|
3973
|
-
\u274C ${flowName} cancelled by user`);
|
|
4290
|
+
\u274C ${options.copy.flowName} cancelled by user`);
|
|
3974
4291
|
}
|
|
3975
4292
|
throw error;
|
|
3976
4293
|
}
|
|
@@ -3978,12 +4295,22 @@ async function runOauthFlowEntryPoint({
|
|
|
3978
4295
|
function withRecoveryMessage(message, recoveryMessage) {
|
|
3979
4296
|
return recoveryMessage ? `${message} ${recoveryMessage}` : message;
|
|
3980
4297
|
}
|
|
4298
|
+
function createMissingHeadlessCallbackUrlError() {
|
|
4299
|
+
return new OauthCallbackError({
|
|
4300
|
+
kind: "missing_callback_url",
|
|
4301
|
+
message: "Paste the final OAuth callback URL from your browser."
|
|
4302
|
+
});
|
|
4303
|
+
}
|
|
4304
|
+
function writeHeadlessOauthStatus(message) {
|
|
4305
|
+
process.stderr.write(`${message}
|
|
4306
|
+
`);
|
|
4307
|
+
}
|
|
3981
4308
|
async function runOauthFlow({
|
|
3982
4309
|
timeoutMs = LOGIN_TIMEOUT_MS,
|
|
3983
4310
|
pkceCredentials,
|
|
3984
4311
|
baseUrl: baseUrl2,
|
|
3985
4312
|
entryPoint,
|
|
3986
|
-
|
|
4313
|
+
copy,
|
|
3987
4314
|
silent = false,
|
|
3988
4315
|
onProgress
|
|
3989
4316
|
}) {
|
|
@@ -3998,7 +4325,7 @@ async function runOauthFlow({
|
|
|
3998
4325
|
const code = await collectLocalCallbackCode({
|
|
3999
4326
|
transaction,
|
|
4000
4327
|
timeoutMs,
|
|
4001
|
-
|
|
4328
|
+
action: copy.action,
|
|
4002
4329
|
silent,
|
|
4003
4330
|
onProgress
|
|
4004
4331
|
});
|
|
@@ -4012,92 +4339,104 @@ async function runOauthFlow({
|
|
|
4012
4339
|
}
|
|
4013
4340
|
async function readHeadlessCallbackUrl({
|
|
4014
4341
|
timeoutMs,
|
|
4015
|
-
interactive
|
|
4016
|
-
recoveryMessage
|
|
4342
|
+
interactive
|
|
4017
4343
|
}) {
|
|
4018
|
-
const timeoutMessage = withRecoveryMessage(
|
|
4019
|
-
`Signup timed out after ${Math.round(timeoutMs / 1e3)} seconds.`,
|
|
4020
|
-
recoveryMessage
|
|
4021
|
-
);
|
|
4022
|
-
const missingCallbackUrlMessage = withRecoveryMessage(
|
|
4023
|
-
"Paste the final OAuth callback URL from your browser.",
|
|
4024
|
-
recoveryMessage
|
|
4025
|
-
);
|
|
4026
4344
|
const rl = createInterface({ input: process.stdin, output: process.stderr });
|
|
4027
4345
|
const abortController = new AbortController();
|
|
4028
4346
|
const timeoutTimer = setTimeout(() => abortController.abort(), timeoutMs);
|
|
4029
|
-
const readUrl =
|
|
4030
|
-
signal: abortController.signal
|
|
4031
|
-
}) : new Promise((resolve4, reject) => {
|
|
4347
|
+
const readUrl = new Promise((resolve4, reject) => {
|
|
4032
4348
|
let settled = false;
|
|
4033
|
-
const
|
|
4349
|
+
const handleLine = (value) => settleResolve(value);
|
|
4350
|
+
const handleAbort = () => settleReject(new OauthFlowTimeoutError({ timeoutMs }));
|
|
4351
|
+
const handleClose = () => settleReject(createMissingHeadlessCallbackUrlError());
|
|
4352
|
+
const handleError = (error) => settleReject(error);
|
|
4353
|
+
const handleCancellation = () => settleReject(new ZapierCliUserCancellationError());
|
|
4354
|
+
const cleanupListeners = () => {
|
|
4355
|
+
abortController.signal.removeEventListener("abort", handleAbort);
|
|
4356
|
+
rl.off("line", handleLine);
|
|
4357
|
+
rl.off("close", handleClose);
|
|
4358
|
+
rl.off("error", handleError);
|
|
4359
|
+
rl.off("SIGINT", handleCancellation);
|
|
4360
|
+
process.off("SIGINT", handleCancellation);
|
|
4361
|
+
process.off("SIGTERM", handleCancellation);
|
|
4362
|
+
};
|
|
4363
|
+
function settleResolve(value) {
|
|
4364
|
+
if (settled) return;
|
|
4034
4365
|
settled = true;
|
|
4366
|
+
cleanupListeners();
|
|
4035
4367
|
resolve4(value);
|
|
4036
|
-
}
|
|
4037
|
-
|
|
4368
|
+
}
|
|
4369
|
+
function settleReject(error) {
|
|
4038
4370
|
if (settled) return;
|
|
4039
4371
|
settled = true;
|
|
4372
|
+
cleanupListeners();
|
|
4040
4373
|
reject(error);
|
|
4041
|
-
}
|
|
4042
|
-
abortController.signal.addEventListener(
|
|
4043
|
-
|
|
4044
|
-
|
|
4045
|
-
|
|
4046
|
-
);
|
|
4047
|
-
rl.once("
|
|
4048
|
-
|
|
4049
|
-
|
|
4050
|
-
|
|
4051
|
-
|
|
4052
|
-
|
|
4374
|
+
}
|
|
4375
|
+
abortController.signal.addEventListener("abort", handleAbort, {
|
|
4376
|
+
once: true
|
|
4377
|
+
});
|
|
4378
|
+
rl.once("close", handleClose);
|
|
4379
|
+
rl.once("error", handleError);
|
|
4380
|
+
rl.once("SIGINT", handleCancellation);
|
|
4381
|
+
process.once("SIGINT", handleCancellation);
|
|
4382
|
+
process.once("SIGTERM", handleCancellation);
|
|
4383
|
+
if (interactive) {
|
|
4384
|
+
void rl.question("Paste the final OAuth callback URL: ", {
|
|
4385
|
+
signal: abortController.signal
|
|
4386
|
+
}).then(settleResolve).catch((error) => {
|
|
4387
|
+
if (error instanceof Error && error.name === "AbortError") {
|
|
4388
|
+
settleReject(new OauthFlowTimeoutError({ timeoutMs }));
|
|
4389
|
+
return;
|
|
4390
|
+
}
|
|
4391
|
+
settleReject(error);
|
|
4392
|
+
});
|
|
4393
|
+
return;
|
|
4394
|
+
}
|
|
4395
|
+
rl.once("line", handleLine);
|
|
4053
4396
|
});
|
|
4054
4397
|
try {
|
|
4055
|
-
return await readUrl
|
|
4056
|
-
if (error instanceof Error && error.name === "AbortError") {
|
|
4057
|
-
throw new Error(timeoutMessage);
|
|
4058
|
-
}
|
|
4059
|
-
throw error;
|
|
4060
|
-
});
|
|
4398
|
+
return await readUrl;
|
|
4061
4399
|
} finally {
|
|
4062
4400
|
clearTimeout(timeoutTimer);
|
|
4063
4401
|
rl.close();
|
|
4064
4402
|
}
|
|
4065
4403
|
}
|
|
4066
|
-
async function
|
|
4404
|
+
async function runHeadlessOauthFlow({
|
|
4067
4405
|
timeoutMs = LOGIN_TIMEOUT_MS,
|
|
4068
4406
|
pkceCredentials,
|
|
4069
4407
|
baseUrl: baseUrl2,
|
|
4408
|
+
entryPoint,
|
|
4070
4409
|
interactive = true,
|
|
4071
4410
|
onProgress,
|
|
4072
|
-
|
|
4411
|
+
copy
|
|
4073
4412
|
}) {
|
|
4074
4413
|
const port = LOGIN_PORTS[0];
|
|
4075
4414
|
const transaction = await prepareOauthTransaction({
|
|
4076
4415
|
pkceCredentials,
|
|
4077
4416
|
baseUrl: baseUrl2,
|
|
4078
4417
|
redirectUri: `http://${OAUTH_LOOPBACK_HOST}:${port}/oauth`,
|
|
4079
|
-
entryPoint
|
|
4418
|
+
entryPoint
|
|
4080
4419
|
});
|
|
4081
|
-
|
|
4082
|
-
|
|
4420
|
+
writeHeadlessOauthStatus(
|
|
4421
|
+
`Use this mode to ${copy.action} from a machine that has no browser.`
|
|
4422
|
+
);
|
|
4423
|
+
writeHeadlessOauthStatus(
|
|
4424
|
+
`Open this ${copy.urlLabel} in a browser on another machine:`
|
|
4083
4425
|
);
|
|
4084
|
-
console.log("Open this signup URL in a browser on another machine:");
|
|
4085
4426
|
console.log(transaction.browserAuthUrl);
|
|
4086
|
-
|
|
4427
|
+
writeHeadlessOauthStatus(
|
|
4087
4428
|
`When the browser lands on ${transaction.redirectUri} and cannot connect, paste the full final URL back here.`
|
|
4088
4429
|
);
|
|
4089
4430
|
const callbackUrl = await readHeadlessCallbackUrl({
|
|
4090
4431
|
timeoutMs,
|
|
4091
|
-
interactive
|
|
4092
|
-
recoveryMessage
|
|
4432
|
+
interactive
|
|
4093
4433
|
});
|
|
4094
4434
|
const code = getCallbackCode({
|
|
4095
4435
|
callbackUrl,
|
|
4096
|
-
transaction
|
|
4097
|
-
recoveryMessage
|
|
4436
|
+
transaction
|
|
4098
4437
|
});
|
|
4099
4438
|
onProgress?.({ type: "callback_accepted" });
|
|
4100
|
-
|
|
4439
|
+
writeHeadlessOauthStatus("Exchanging authorization code for tokens...");
|
|
4101
4440
|
onProgress?.({ type: "token_exchange_started" });
|
|
4102
4441
|
const tokens = await exchangeOauthCode({ ...transaction, code });
|
|
4103
4442
|
onProgress?.({ type: "token_exchange_completed" });
|
|
@@ -4106,7 +4445,7 @@ async function runHeadlessSignupOauthFlow({
|
|
|
4106
4445
|
async function collectLocalCallbackCode({
|
|
4107
4446
|
transaction,
|
|
4108
4447
|
timeoutMs,
|
|
4109
|
-
|
|
4448
|
+
action,
|
|
4110
4449
|
silent,
|
|
4111
4450
|
onProgress
|
|
4112
4451
|
}) {
|
|
@@ -4114,21 +4453,26 @@ async function collectLocalCallbackCode({
|
|
|
4114
4453
|
const app = express();
|
|
4115
4454
|
app.get("/oauth", (req, res) => {
|
|
4116
4455
|
res.setHeader("Connection", "close");
|
|
4117
|
-
|
|
4118
|
-
|
|
4119
|
-
|
|
4120
|
-
|
|
4121
|
-
|
|
4122
|
-
|
|
4123
|
-
|
|
4124
|
-
);
|
|
4125
|
-
|
|
4126
|
-
} else if (!req.query.code) {
|
|
4127
|
-
reject(new Error("No authorization code received"));
|
|
4128
|
-
res.end("No authorization code received. You can close this tab.");
|
|
4129
|
-
} else {
|
|
4130
|
-
resolve4(String(req.query.code));
|
|
4456
|
+
try {
|
|
4457
|
+
const code = getCallbackCode({
|
|
4458
|
+
callbackUrl: new URL(
|
|
4459
|
+
req.originalUrl,
|
|
4460
|
+
transaction.redirectUri
|
|
4461
|
+
).toString(),
|
|
4462
|
+
transaction
|
|
4463
|
+
});
|
|
4464
|
+
resolve4(code);
|
|
4131
4465
|
res.end("You can now close this tab and return to the CLI.");
|
|
4466
|
+
} catch (error) {
|
|
4467
|
+
if (error instanceof OauthCallbackError && error.kind === "state_mismatch") {
|
|
4468
|
+
res.status(400).end("Invalid state. You can close this tab.");
|
|
4469
|
+
} else if (error instanceof OauthAuthorizationDeniedError) {
|
|
4470
|
+
reject(error);
|
|
4471
|
+
res.end("Authorization was denied. You can close this tab.");
|
|
4472
|
+
} else {
|
|
4473
|
+
reject(error);
|
|
4474
|
+
res.end("No authorization code received. You can close this tab.");
|
|
4475
|
+
}
|
|
4132
4476
|
}
|
|
4133
4477
|
});
|
|
4134
4478
|
const server = app.listen(
|
|
@@ -4149,19 +4493,19 @@ async function collectLocalCallbackCode({
|
|
|
4149
4493
|
let timeoutTimer;
|
|
4150
4494
|
try {
|
|
4151
4495
|
await waitForServerListening(server);
|
|
4152
|
-
await openBrowser({ transaction,
|
|
4496
|
+
await openBrowser({ transaction, action, silent, onProgress });
|
|
4153
4497
|
const waitForCode = Promise.race([
|
|
4154
4498
|
promise,
|
|
4155
4499
|
new Promise((_resolve, rejectTimeout) => {
|
|
4156
4500
|
timeoutTimer = setTimeout(() => {
|
|
4157
|
-
rejectTimeout(new OauthFlowTimeoutError(timeoutMs));
|
|
4501
|
+
rejectTimeout(new OauthFlowTimeoutError({ timeoutMs }));
|
|
4158
4502
|
}, timeoutMs);
|
|
4159
4503
|
})
|
|
4160
4504
|
]);
|
|
4161
4505
|
onProgress?.({ type: "callback_waiting" });
|
|
4162
4506
|
return silent ? await waitForCode : await spinPromise(
|
|
4163
4507
|
waitForCode,
|
|
4164
|
-
`Waiting for you to ${
|
|
4508
|
+
`Waiting for you to ${action} and authorize`
|
|
4165
4509
|
);
|
|
4166
4510
|
} finally {
|
|
4167
4511
|
if (timeoutTimer) clearTimeout(timeoutTimer);
|
|
@@ -4191,12 +4535,12 @@ async function waitForServerListening(server) {
|
|
|
4191
4535
|
}
|
|
4192
4536
|
async function openBrowser({
|
|
4193
4537
|
transaction,
|
|
4194
|
-
|
|
4538
|
+
action,
|
|
4195
4539
|
silent,
|
|
4196
4540
|
onProgress
|
|
4197
4541
|
}) {
|
|
4198
4542
|
if (!silent) {
|
|
4199
|
-
log_default.info(`Opening your browser to ${
|
|
4543
|
+
log_default.info(`Opening your browser to ${action}.`);
|
|
4200
4544
|
log_default.info("If it doesn't open, visit:", transaction.browserAuthUrl);
|
|
4201
4545
|
}
|
|
4202
4546
|
onProgress?.({ type: "browser_opening", url: transaction.browserAuthUrl });
|
|
@@ -4206,9 +4550,7 @@ async function openBrowser({
|
|
|
4206
4550
|
} catch (err) {
|
|
4207
4551
|
const reason = err instanceof Error ? err.message : String(err);
|
|
4208
4552
|
if (!silent) {
|
|
4209
|
-
log_default.info(
|
|
4210
|
-
`Browser did not open automatically to ${authAction}: ${reason}`
|
|
4211
|
-
);
|
|
4553
|
+
log_default.info(`Browser did not open automatically to ${action}: ${reason}`);
|
|
4212
4554
|
log_default.info("Visit this URL manually:", transaction.browserAuthUrl);
|
|
4213
4555
|
}
|
|
4214
4556
|
onProgress?.({
|
|
@@ -4237,61 +4579,10 @@ async function closeServer({
|
|
|
4237
4579
|
});
|
|
4238
4580
|
}
|
|
4239
4581
|
|
|
4240
|
-
// src/utils/auth/oauth-errors.ts
|
|
4241
|
-
var SENSITIVE_OAUTH_FIELDS = [
|
|
4242
|
-
"access_token",
|
|
4243
|
-
"refresh_token",
|
|
4244
|
-
"id_token",
|
|
4245
|
-
"client_secret",
|
|
4246
|
-
"code_verifier",
|
|
4247
|
-
"code_challenge"
|
|
4248
|
-
];
|
|
4249
|
-
function getErrorMessage(error) {
|
|
4250
|
-
return error instanceof Error ? error.message : String(error);
|
|
4251
|
-
}
|
|
4252
|
-
function toCamelCase(field) {
|
|
4253
|
-
return field.replace(
|
|
4254
|
-
/_([a-z])/g,
|
|
4255
|
-
(_match, letter) => letter.toUpperCase()
|
|
4256
|
-
);
|
|
4257
|
-
}
|
|
4258
|
-
function escapeRegExp(value) {
|
|
4259
|
-
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
4260
|
-
}
|
|
4261
|
-
var sensitiveOauthFieldPattern = Array.from(
|
|
4262
|
-
new Set(
|
|
4263
|
-
SENSITIVE_OAUTH_FIELDS.flatMap((field) => [field, toCamelCase(field)])
|
|
4264
|
-
)
|
|
4265
|
-
).map(escapeRegExp).join("|");
|
|
4266
|
-
var sensitiveQueryParamPattern = new RegExp(
|
|
4267
|
-
`([?&])(${sensitiveOauthFieldPattern})(=)[^&#\\s"'<>]*`,
|
|
4268
|
-
"gi"
|
|
4269
|
-
);
|
|
4270
|
-
function redactSensitiveOauthErrorMessage(message) {
|
|
4271
|
-
return message.replace(
|
|
4272
|
-
sensitiveQueryParamPattern,
|
|
4273
|
-
(_match, prefix, key, separator) => `${prefix}${key}${separator}[REDACTED]`
|
|
4274
|
-
).replace(
|
|
4275
|
-
new RegExp(`"(${sensitiveOauthFieldPattern})"(\\s*:\\s*)"[^"]*"`, "g"),
|
|
4276
|
-
(_match, key, separator) => `"${key}"${separator}"[REDACTED]"`
|
|
4277
|
-
);
|
|
4278
|
-
}
|
|
4279
|
-
function toRedactedOauthError(error) {
|
|
4280
|
-
const message = redactSensitiveOauthErrorMessage(getErrorMessage(error));
|
|
4281
|
-
if (error instanceof ZapierCliValidationError) {
|
|
4282
|
-
return new ZapierCliValidationError(message);
|
|
4283
|
-
}
|
|
4284
|
-
if (error instanceof Error) {
|
|
4285
|
-
const redactedError = new Error(message);
|
|
4286
|
-
redactedError.name = error.name;
|
|
4287
|
-
return redactedError;
|
|
4288
|
-
}
|
|
4289
|
-
return new ZapierCliValidationError(message);
|
|
4290
|
-
}
|
|
4291
|
-
|
|
4292
4582
|
// src/utils/auth/account-auth.ts
|
|
4293
4583
|
var LEGACY_JWT_UPGRADE_PROMPT = "We're upgrading your login to client credentials for a simpler, more reliable experience and to support future security controls. Older Zapier SDK/CLI versions on this machine may stop working after the upgrade. Continue?";
|
|
4294
4584
|
var SIGNUP_RECOVERY_MESSAGE = "Restart `zapier-sdk signup` to generate a fresh signup URL and try again.";
|
|
4585
|
+
var HEADLESS_LOGIN_RECOVERY_MESSAGE = "Restart `zapier-sdk login --headless` to generate a fresh login URL and try again.";
|
|
4295
4586
|
var HEADLESS_SIGNUP_RECOVERY_MESSAGE = "Restart `zapier-sdk signup --headless` to generate a fresh signup URL and try again.";
|
|
4296
4587
|
function getEntryPointLabel(entryPoint) {
|
|
4297
4588
|
return entryPoint === "signup" ? "Signup" : "Login";
|
|
@@ -4411,7 +4702,8 @@ Logging out will delete these credentials and may interrupt other Zapier SDK or
|
|
|
4411
4702
|
Log out and ${getActiveCredentialsAction(entryPoint)}?`
|
|
4412
4703
|
}) : promptlessCredentialResetError(activeCredentials);
|
|
4413
4704
|
if (!confirmed) {
|
|
4414
|
-
|
|
4705
|
+
process.stderr.write(`${flowLabel} cancelled.
|
|
4706
|
+
`);
|
|
4415
4707
|
return false;
|
|
4416
4708
|
}
|
|
4417
4709
|
try {
|
|
@@ -4430,7 +4722,8 @@ Log out and ${getActiveCredentialsAction(entryPoint)}?`
|
|
|
4430
4722
|
message: `${flowLabel} cleanup failed. Reset local session state and continue?`
|
|
4431
4723
|
});
|
|
4432
4724
|
if (!reset) {
|
|
4433
|
-
|
|
4725
|
+
process.stderr.write(`${flowLabel} cancelled.
|
|
4726
|
+
`);
|
|
4434
4727
|
return false;
|
|
4435
4728
|
}
|
|
4436
4729
|
await deleteStoredClientCredentials({
|
|
@@ -4444,7 +4737,8 @@ Log out and ${getActiveCredentialsAction(entryPoint)}?`
|
|
|
4444
4737
|
message: LEGACY_JWT_UPGRADE_PROMPT
|
|
4445
4738
|
}) : promptlessLegacyJwtUpgradeError();
|
|
4446
4739
|
if (!confirmed) {
|
|
4447
|
-
|
|
4740
|
+
process.stderr.write(`${flowLabel} cancelled.
|
|
4741
|
+
`);
|
|
4448
4742
|
return false;
|
|
4449
4743
|
}
|
|
4450
4744
|
}
|
|
@@ -4539,7 +4833,14 @@ async function runOauthForEntryPoint({
|
|
|
4539
4833
|
);
|
|
4540
4834
|
}
|
|
4541
4835
|
return runOauthWithRedaction(
|
|
4542
|
-
() => runLoginOauthFlow({
|
|
4836
|
+
() => runLoginOauthFlow({
|
|
4837
|
+
timeoutMs,
|
|
4838
|
+
pkceCredentials,
|
|
4839
|
+
baseUrl: baseUrl2,
|
|
4840
|
+
headless,
|
|
4841
|
+
interactive,
|
|
4842
|
+
recoveryMessage: headless ? HEADLESS_LOGIN_RECOVERY_MESSAGE : void 0
|
|
4843
|
+
})
|
|
4543
4844
|
);
|
|
4544
4845
|
}
|
|
4545
4846
|
async function runAccountAuth({
|
|
@@ -4551,6 +4852,7 @@ async function runAccountAuth({
|
|
|
4551
4852
|
const interactive = !resolveNonInteractive(options);
|
|
4552
4853
|
const resolvedCredentials = await sdk.context.resolveCredentials();
|
|
4553
4854
|
const pkceCredentials = toPkceCredentials(resolvedCredentials);
|
|
4855
|
+
const headless = options.headless === true;
|
|
4554
4856
|
const credentialsBaseUrl2 = await resolveCredentialsBaseUrl({
|
|
4555
4857
|
...sdk.context,
|
|
4556
4858
|
resolvedCredentials
|
|
@@ -4580,7 +4882,7 @@ async function runAccountAuth({
|
|
|
4580
4882
|
timeoutMs: timeoutSeconds * 1e3,
|
|
4581
4883
|
pkceCredentials,
|
|
4582
4884
|
baseUrl: credentialsBaseUrl2,
|
|
4583
|
-
headless
|
|
4885
|
+
headless,
|
|
4584
4886
|
interactive
|
|
4585
4887
|
});
|
|
4586
4888
|
const scopedApi = getOrCreateApiClient({
|
|
@@ -4588,9 +4890,10 @@ async function runAccountAuth({
|
|
|
4588
4890
|
baseUrl: credentialsBaseUrl2
|
|
4589
4891
|
});
|
|
4590
4892
|
const profile = await getProfile(scopedApi);
|
|
4591
|
-
|
|
4592
|
-
|
|
4593
|
-
|
|
4893
|
+
process.stderr.write(`${getProfileMessage(entryPoint, profile.email)}
|
|
4894
|
+
`);
|
|
4895
|
+
process.stderr.write(
|
|
4896
|
+
"\nGenerating credentials so this machine can make authenticated requests on your behalf.\n"
|
|
4594
4897
|
);
|
|
4595
4898
|
const credentialName = providedName ?? await resolveCredentialName({
|
|
4596
4899
|
email: profile.email,
|
|
@@ -4606,11 +4909,12 @@ async function runAccountAuth({
|
|
|
4606
4909
|
useApprovals,
|
|
4607
4910
|
cleanupLogPrefix: entryPoint
|
|
4608
4911
|
});
|
|
4609
|
-
|
|
4610
|
-
`\u2705 Credentials "${credentialName}" created and set as default. You are ready to use the Zapier SDK
|
|
4912
|
+
process.stderr.write(
|
|
4913
|
+
`\u2705 Credentials "${credentialName}" created and set as default. You are ready to use the Zapier SDK.
|
|
4914
|
+
`
|
|
4611
4915
|
);
|
|
4612
4916
|
if (useApprovals) {
|
|
4613
|
-
|
|
4917
|
+
process.stderr.write("\u{1F510} Approvals are enabled for these credentials.\n");
|
|
4614
4918
|
}
|
|
4615
4919
|
emitAccountAuthSuccess({ sdk, profile, clientId });
|
|
4616
4920
|
}
|
|
@@ -4629,7 +4933,10 @@ var LoginSchema = z.object({
|
|
|
4629
4933
|
skipPrompts: z.boolean().optional().meta({
|
|
4630
4934
|
deprecated: true,
|
|
4631
4935
|
deprecationMessage: "Use --non-interactive instead."
|
|
4632
|
-
})
|
|
4936
|
+
}),
|
|
4937
|
+
headless: z.boolean().optional().describe(
|
|
4938
|
+
"Use when logging in from a machine that has no browser. Prints a login link to open elsewhere, then accepts the pasted loopback callback URL."
|
|
4939
|
+
)
|
|
4633
4940
|
}).describe("Log in to Zapier to access your account");
|
|
4634
4941
|
|
|
4635
4942
|
// src/plugins/login/index.ts
|
|
@@ -7261,7 +7568,7 @@ function buildBoxLines(message) {
|
|
|
7261
7568
|
// package.json with { type: 'json' }
|
|
7262
7569
|
var package_default2 = {
|
|
7263
7570
|
name: "@zapier/zapier-sdk-cli",
|
|
7264
|
-
version: "0.
|
|
7571
|
+
version: "0.61.1"};
|
|
7265
7572
|
|
|
7266
7573
|
// src/sdk.ts
|
|
7267
7574
|
injectCliLogin(login_exports);
|
|
@@ -7338,6 +7645,9 @@ function createZapierCliSdk2(options = {}) {
|
|
|
7338
7645
|
|
|
7339
7646
|
// src/utils/extensions.ts
|
|
7340
7647
|
var ENV_VAR = "ZAPIER_SDK_EXTENSIONS";
|
|
7648
|
+
function isModelPlugin(value) {
|
|
7649
|
+
return typeof value === "object" && value !== null && "pluginType" in value && typeof value.pluginType === "string";
|
|
7650
|
+
}
|
|
7341
7651
|
async function resolveExtensions() {
|
|
7342
7652
|
const seen = /* @__PURE__ */ new Set();
|
|
7343
7653
|
const specs = readEnvSpecs().filter((spec) => {
|
|
@@ -7370,8 +7680,11 @@ function normalizeExtension(exported) {
|
|
|
7370
7680
|
if (typeof exported === "function") {
|
|
7371
7681
|
return [exported];
|
|
7372
7682
|
}
|
|
7683
|
+
if (isModelPlugin(exported)) {
|
|
7684
|
+
return [exported];
|
|
7685
|
+
}
|
|
7373
7686
|
if (Array.isArray(exported)) {
|
|
7374
|
-
if (exported.every((e) => typeof e === "function")) {
|
|
7687
|
+
if (exported.every((e) => typeof e === "function" || isModelPlugin(e))) {
|
|
7375
7688
|
return exported;
|
|
7376
7689
|
}
|
|
7377
7690
|
return null;
|