@zapier/zapier-sdk-cli 0.86.6 → 0.87.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 +4 -4
- package/CHANGELOG.md +30 -0
- package/README.md +40 -3
- package/dist/cli.cjs +801 -369
- package/dist/cli.mjs +805 -373
- package/dist/experimental.cjs +798 -364
- package/dist/experimental.d.mts +1 -1
- package/dist/experimental.d.ts +1 -1
- package/dist/experimental.mjs +801 -367
- package/dist/{extensions-OFaHmWVC.d.mts → extensions-CrfC6xjA.d.mts} +23 -2
- package/dist/{extensions-OFaHmWVC.d.ts → extensions-CrfC6xjA.d.ts} +23 -2
- package/dist/index.cjs +798 -368
- package/dist/index.d.mts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.mjs +802 -372
- package/package.json +3 -3
package/dist/experimental.mjs
CHANGED
|
@@ -2,14 +2,14 @@ import * as jwt from 'jsonwebtoken';
|
|
|
2
2
|
import { deletePassword, getPassword, getKeyring, setPassword } from 'cross-keychain';
|
|
3
3
|
import Conf from 'conf';
|
|
4
4
|
import * as fs from 'fs';
|
|
5
|
-
import { promises, createWriteStream, existsSync, readdirSync, readFileSync, rmSync, mkdirSync, writeFileSync, copyFileSync } from 'fs';
|
|
5
|
+
import { promises, createWriteStream, existsSync, readdirSync, realpathSync, readFileSync, rmSync, mkdirSync, writeFileSync, copyFileSync } from 'fs';
|
|
6
6
|
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, apiPlugin, defineMethod, apiPluginRef, resolveCredentialsPluginRef, eventEmissionPluginRef, sdkOptionsPluginRef, declareOptionalProperty, OutputPropertySchema, ZapierBundleError, isZapierBundleError, DEFAULT_CONFIG_PATH, declareMethod, ZapierValidationError, isZapierValidationError, ZapierUnknownError, manifestPluginRef, defineResolver, isPermanentHttpError, isZapierAuthenticationError, ZapierApiError, defineOverride, fetchPlugin, zapierCoreOptions, injectCliLogin, omitExports, zapierSdkPlugin, invalidateCachedToken, batch, toSnakeCase, ZapierError, isZapierReleaseTriggerMessageSignal, createSdk, SDK_OPTIONS_ID, resolvePlugin, isCredentialsObject, getRegistryPlugin, createController, ZapierAbortDrainSignal, ZapierReleaseTriggerMessageSignal, buildApplicationLifecycleEvent, AuthMechanism, isCoreCancelledSignal, ZapierAuthenticationError, STABILITY_DEFINITIONS, DEPRECATION_NOTICE_EVENT } from '@zapier/zapier-sdk';
|
|
10
|
+
import { definePlugin, apiPlugin, defineMethod, apiPluginRef, resolveCredentialsPluginRef, eventEmissionPluginRef, sdkOptionsPluginRef, declareOptionalProperty, OutputPropertySchema, ZapierBundleError, isZapierBundleError, DEFAULT_CONFIG_PATH, declareMethod, ZapierValidationError, isZapierValidationError, ZapierUnknownError, manifestPluginRef, defineResolver, isPermanentHttpError, isZapierAuthenticationError, ZapierApiError, defineOverride, fetchPlugin, zapierCoreOptions, injectCliLogin, omitExports, zapierSdkPlugin, invalidateCachedToken, batch, toSnakeCase, ZapierError, isZapierReleaseTriggerMessageSignal, createSdk, SDK_OPTIONS_ID, resolvePlugin, isCredentialsObject, isZapierSignal, getRegistryPlugin, createController, ZapierAbortDrainSignal, ZapierReleaseTriggerMessageSignal, buildApplicationLifecycleEvent, AuthMechanism, ZapierSignal, isCoreCancelledSignal, ZapierAuthenticationError, STABILITY_DEFINITIONS, DEPRECATION_NOTICE_EVENT } from '@zapier/zapier-sdk';
|
|
11
11
|
import { z } from 'zod';
|
|
12
|
-
import { triggerInboxResolver, DrainTriggerInboxSchema, WatchTriggerInboxSchema,
|
|
12
|
+
import { triggerInboxResolver, DrainTriggerInboxSchema, WatchTriggerInboxSchema, injectCliLogin as injectCliLogin$1, definePlugin as definePlugin$1, omitExports as omitExports$1, zapierExperimentalSdkPlugin, createSdk as createSdk$1, CORE_OPTIONS_ID, SDK_OPTIONS_ID as SDK_OPTIONS_ID$1, addPlugin, formatExtensionPluginFailure } from '@zapier/zapier-sdk/experimental';
|
|
13
13
|
import { hostname } from 'os';
|
|
14
14
|
import inquirer4 from 'inquirer';
|
|
15
15
|
import chalk5 from 'chalk';
|
|
@@ -31,6 +31,7 @@ import packageJsonLib, { VersionNotFoundError } from 'package-json';
|
|
|
31
31
|
import semver from 'semver';
|
|
32
32
|
import crossSpawn from 'cross-spawn';
|
|
33
33
|
import wrapAnsi from 'wrap-ansi';
|
|
34
|
+
import { createRequire } from 'module';
|
|
34
35
|
import Table from 'cli-table3';
|
|
35
36
|
|
|
36
37
|
var __defProp = Object.defineProperty;
|
|
@@ -994,6 +995,7 @@ var SENSITIVE_FLAGS = [
|
|
|
994
995
|
"--credentials-client-id",
|
|
995
996
|
"--credentials-base-url",
|
|
996
997
|
"--callback-url",
|
|
998
|
+
"--answer",
|
|
997
999
|
"--user",
|
|
998
1000
|
"--header",
|
|
999
1001
|
"-H",
|
|
@@ -1230,6 +1232,7 @@ var searchSelect = createPrompt(
|
|
|
1230
1232
|
);
|
|
1231
1233
|
|
|
1232
1234
|
// src/utils/auth/credentials-picker.ts
|
|
1235
|
+
var CREDENTIALS_PICKER_QUESTION = "Select credentials or add a new account:";
|
|
1233
1236
|
function isSameCredentials(left, right) {
|
|
1234
1237
|
return left.name === right.name && left.baseUrl === right.baseUrl;
|
|
1235
1238
|
}
|
|
@@ -1335,7 +1338,7 @@ async function promptForCredentials({
|
|
|
1335
1338
|
activeCredentials
|
|
1336
1339
|
}) {
|
|
1337
1340
|
return searchSelect({
|
|
1338
|
-
message:
|
|
1341
|
+
message: CREDENTIALS_PICKER_QUESTION,
|
|
1339
1342
|
source: (term) => buildCredentialsChoices({
|
|
1340
1343
|
credentials,
|
|
1341
1344
|
activeCredentials,
|
|
@@ -1398,14 +1401,15 @@ async function switchCredentials({
|
|
|
1398
1401
|
async function chooseStoredLoginCredentialsAtBaseUrl({
|
|
1399
1402
|
baseUrl,
|
|
1400
1403
|
options,
|
|
1401
|
-
resolvedCredentials
|
|
1404
|
+
resolvedCredentials,
|
|
1405
|
+
prompt = promptForCredentials
|
|
1402
1406
|
}) {
|
|
1403
1407
|
if (resolvedCredentials !== void 0 || authFlowOnlyOptions(options).length > 0) {
|
|
1404
1408
|
return "none";
|
|
1405
1409
|
}
|
|
1406
1410
|
const storedCredentials = listStoredCredentials({ baseUrl });
|
|
1407
1411
|
if (storedCredentials.length === 0) return "none";
|
|
1408
|
-
const selection = await
|
|
1412
|
+
const selection = await prompt({
|
|
1409
1413
|
credentials: storedCredentials,
|
|
1410
1414
|
activeCredentials: getActiveCredentials({ baseUrl })
|
|
1411
1415
|
});
|
|
@@ -1419,7 +1423,8 @@ async function chooseStoredLoginCredentialsAtBaseUrl({
|
|
|
1419
1423
|
}
|
|
1420
1424
|
async function chooseStoredLoginCredentials({
|
|
1421
1425
|
imports,
|
|
1422
|
-
options
|
|
1426
|
+
options,
|
|
1427
|
+
prompt
|
|
1423
1428
|
}) {
|
|
1424
1429
|
const resolvedCredentials = await imports.resolveCredentials();
|
|
1425
1430
|
const credentialsBaseUrl = await resolveCredentialsBaseUrl({
|
|
@@ -1429,7 +1434,8 @@ async function chooseStoredLoginCredentials({
|
|
|
1429
1434
|
return chooseStoredLoginCredentialsAtBaseUrl({
|
|
1430
1435
|
baseUrl: credentialsBaseUrl,
|
|
1431
1436
|
options,
|
|
1432
|
-
resolvedCredentials
|
|
1437
|
+
resolvedCredentials,
|
|
1438
|
+
prompt
|
|
1433
1439
|
});
|
|
1434
1440
|
}
|
|
1435
1441
|
async function confirmNewCredentialsName(name) {
|
|
@@ -1800,6 +1806,7 @@ var PendingOauthTransactionSchema = z.object({
|
|
|
1800
1806
|
credentialName: z.string().optional(),
|
|
1801
1807
|
useApprovals: z.boolean(),
|
|
1802
1808
|
headless: z.boolean().default(false),
|
|
1809
|
+
preserveExistingCredentials: z.boolean().default(false),
|
|
1803
1810
|
transaction: OauthTransactionSchema
|
|
1804
1811
|
});
|
|
1805
1812
|
function savePendingOauthTransaction({
|
|
@@ -1809,7 +1816,8 @@ function savePendingOauthTransaction({
|
|
|
1809
1816
|
credentialsBaseUrl,
|
|
1810
1817
|
credentialName,
|
|
1811
1818
|
useApprovals,
|
|
1812
|
-
headless
|
|
1819
|
+
headless,
|
|
1820
|
+
preserveExistingCredentials = false
|
|
1813
1821
|
}) {
|
|
1814
1822
|
const pendingTransaction = {
|
|
1815
1823
|
version: 1,
|
|
@@ -1817,6 +1825,7 @@ function savePendingOauthTransaction({
|
|
|
1817
1825
|
expiresAt,
|
|
1818
1826
|
useApprovals,
|
|
1819
1827
|
headless,
|
|
1828
|
+
preserveExistingCredentials,
|
|
1820
1829
|
transaction,
|
|
1821
1830
|
...credentialsBaseUrl !== void 0 && { credentialsBaseUrl },
|
|
1822
1831
|
...credentialName !== void 0 && { credentialName }
|
|
@@ -1835,6 +1844,7 @@ function clearPendingOauthTransaction() {
|
|
|
1835
1844
|
}
|
|
1836
1845
|
|
|
1837
1846
|
// src/utils/auth/oauth-flow.ts
|
|
1847
|
+
var OAUTH_CALLBACK_QUESTION = "Paste the final OAuth callback URL:";
|
|
1838
1848
|
var PENDING_OAUTH_RECOVERY_MESSAGE = "Start a new non-interactive login or signup flow to generate a fresh URL.";
|
|
1839
1849
|
var LOGIN_OAUTH_COPY = {
|
|
1840
1850
|
flowName: "Login",
|
|
@@ -1898,7 +1908,9 @@ async function startPendingOauthFlow({
|
|
|
1898
1908
|
entryPoint,
|
|
1899
1909
|
credentialName,
|
|
1900
1910
|
useApprovals,
|
|
1901
|
-
headless
|
|
1911
|
+
headless,
|
|
1912
|
+
preserveExistingCredentials = false,
|
|
1913
|
+
silent = false
|
|
1902
1914
|
}) {
|
|
1903
1915
|
const copy = entryPoint === "signup" ? SIGNUP_OAUTH_COPY : LOGIN_OAUTH_COPY;
|
|
1904
1916
|
const port = LOGIN_PORTS[0];
|
|
@@ -1915,8 +1927,10 @@ async function startPendingOauthFlow({
|
|
|
1915
1927
|
credentialsBaseUrl: baseUrl,
|
|
1916
1928
|
credentialName,
|
|
1917
1929
|
useApprovals,
|
|
1918
|
-
headless
|
|
1930
|
+
headless,
|
|
1931
|
+
preserveExistingCredentials
|
|
1919
1932
|
});
|
|
1933
|
+
if (silent) return pending;
|
|
1920
1934
|
process.stderr.write(
|
|
1921
1935
|
`Open this ${copy.urlLabel} in a browser to continue:
|
|
1922
1936
|
`
|
|
@@ -1942,23 +1956,24 @@ async function startPendingOauthFlow({
|
|
|
1942
1956
|
return pending;
|
|
1943
1957
|
}
|
|
1944
1958
|
function getPendingOauthFlow({
|
|
1945
|
-
entryPoint
|
|
1959
|
+
entryPoint,
|
|
1960
|
+
recoveryMessage = PENDING_OAUTH_RECOVERY_MESSAGE
|
|
1946
1961
|
}) {
|
|
1947
1962
|
const pending = getPendingOauthTransaction();
|
|
1948
1963
|
if (!pending) {
|
|
1949
1964
|
throw new ZapierCliValidationError(
|
|
1950
|
-
`No pending ${entryPoint} OAuth flow found. ${
|
|
1965
|
+
`No pending ${entryPoint} OAuth flow found. ${recoveryMessage}`
|
|
1951
1966
|
);
|
|
1952
1967
|
}
|
|
1953
1968
|
if (Date.now() > pending.expiresAt) {
|
|
1954
1969
|
clearPendingOauthTransaction();
|
|
1955
1970
|
throw new ZapierCliValidationError(
|
|
1956
|
-
`Pending ${pending.entryPoint} OAuth flow has expired. ${
|
|
1971
|
+
`Pending ${pending.entryPoint} OAuth flow has expired. ${recoveryMessage}`
|
|
1957
1972
|
);
|
|
1958
1973
|
}
|
|
1959
1974
|
if (pending.entryPoint !== entryPoint) {
|
|
1960
1975
|
throw new ZapierCliValidationError(
|
|
1961
|
-
`Pending OAuth flow is for ${pending.entryPoint}, not ${entryPoint}. ${
|
|
1976
|
+
`Pending OAuth flow is for ${pending.entryPoint}, not ${entryPoint}. ${recoveryMessage}`
|
|
1962
1977
|
);
|
|
1963
1978
|
}
|
|
1964
1979
|
return pending;
|
|
@@ -2121,7 +2136,7 @@ async function readHeadlessCallbackUrl({
|
|
|
2121
2136
|
process.once("SIGINT", handleCancellation);
|
|
2122
2137
|
process.once("SIGTERM", handleCancellation);
|
|
2123
2138
|
if (interactive) {
|
|
2124
|
-
void rl.question(
|
|
2139
|
+
void rl.question(`${OAUTH_CALLBACK_QUESTION} `, {
|
|
2125
2140
|
signal: abortController.signal
|
|
2126
2141
|
}).then(settleResolve).catch((error) => {
|
|
2127
2142
|
if (error instanceof Error && error.name === "AbortError") {
|
|
@@ -2608,7 +2623,7 @@ async function runOauthForEntryPoint({
|
|
|
2608
2623
|
if (event.type === "callback_accepted") {
|
|
2609
2624
|
emitSignupSuccess({
|
|
2610
2625
|
imports,
|
|
2611
|
-
isNonInteractive:
|
|
2626
|
+
isNonInteractive: !interactive,
|
|
2612
2627
|
isHeadless: headless === true
|
|
2613
2628
|
});
|
|
2614
2629
|
}
|
|
@@ -2690,7 +2705,9 @@ async function runAccountAuth({
|
|
|
2690
2705
|
options,
|
|
2691
2706
|
entryPoint,
|
|
2692
2707
|
storedCredentialsMode = "choose",
|
|
2693
|
-
preserveExistingCredentials = false
|
|
2708
|
+
preserveExistingCredentials = false,
|
|
2709
|
+
useBrowserCallback = false,
|
|
2710
|
+
silent = false
|
|
2694
2711
|
}) {
|
|
2695
2712
|
if (options.callbackUrl !== void 0) {
|
|
2696
2713
|
const { callbackUrl } = options;
|
|
@@ -2723,7 +2740,8 @@ async function runAccountAuth({
|
|
|
2723
2740
|
imports,
|
|
2724
2741
|
baseUrl: pending.credentialsBaseUrl,
|
|
2725
2742
|
interactive: false,
|
|
2726
|
-
entryPoint
|
|
2743
|
+
entryPoint,
|
|
2744
|
+
preserveExistingCredentials: pending.preserveExistingCredentials
|
|
2727
2745
|
})) {
|
|
2728
2746
|
return;
|
|
2729
2747
|
}
|
|
@@ -2800,7 +2818,7 @@ async function runAccountAuth({
|
|
|
2800
2818
|
);
|
|
2801
2819
|
}
|
|
2802
2820
|
}
|
|
2803
|
-
if (entryPoint === "login" && !interactive && providedName === void 0) {
|
|
2821
|
+
if (entryPoint === "login" && !interactive && providedName === void 0 && !preserveExistingCredentials) {
|
|
2804
2822
|
const activeCredentials = getActiveCredentials({
|
|
2805
2823
|
baseUrl: credentialsBaseUrl
|
|
2806
2824
|
});
|
|
@@ -2817,7 +2835,7 @@ async function runAccountAuth({
|
|
|
2817
2835
|
}
|
|
2818
2836
|
const timeoutSeconds = parseTimeoutSeconds(options.timeout);
|
|
2819
2837
|
const useApprovals = options.useApprovals === true;
|
|
2820
|
-
if (!interactive) {
|
|
2838
|
+
if (!interactive && (headless || !useBrowserCallback)) {
|
|
2821
2839
|
await startPendingOauthFlow({
|
|
2822
2840
|
entryPoint,
|
|
2823
2841
|
timeoutMs: timeoutSeconds * 1e3,
|
|
@@ -2825,7 +2843,9 @@ async function runAccountAuth({
|
|
|
2825
2843
|
baseUrl: credentialsBaseUrl,
|
|
2826
2844
|
credentialName: providedName,
|
|
2827
2845
|
useApprovals,
|
|
2828
|
-
headless
|
|
2846
|
+
headless,
|
|
2847
|
+
preserveExistingCredentials,
|
|
2848
|
+
silent
|
|
2829
2849
|
});
|
|
2830
2850
|
return;
|
|
2831
2851
|
}
|
|
@@ -2846,7 +2866,7 @@ async function runAccountAuth({
|
|
|
2846
2866
|
credentialName: providedName,
|
|
2847
2867
|
interactive,
|
|
2848
2868
|
useApprovals,
|
|
2849
|
-
isNonInteractive:
|
|
2869
|
+
isNonInteractive: !interactive,
|
|
2850
2870
|
isHeadless: headless
|
|
2851
2871
|
});
|
|
2852
2872
|
}
|
|
@@ -4737,6 +4757,7 @@ function createProjectDir({
|
|
|
4737
4757
|
var TEMPLATES_DIR = fileURLToPath(
|
|
4738
4758
|
new URL("../templates", import.meta.url)
|
|
4739
4759
|
);
|
|
4760
|
+
var SOURCE_CLI_PATH = import.meta.url.endsWith(".ts") ? fileURLToPath(new URL("./cli.ts", import.meta.url)) : void 0;
|
|
4740
4761
|
|
|
4741
4762
|
// src/plugins/init/steps.ts
|
|
4742
4763
|
var DEFAULT_TEMPLATE = "basic";
|
|
@@ -5447,84 +5468,6 @@ async function checkForUpdates({
|
|
|
5447
5468
|
};
|
|
5448
5469
|
}
|
|
5449
5470
|
}
|
|
5450
|
-
var BRAILLE_BLANK = "\u2800";
|
|
5451
|
-
var LIGHTNING_INTERVAL_MILLISECONDS = 140;
|
|
5452
|
-
var boltRows = [
|
|
5453
|
-
"\u2800\u2800\u2880\u28FE\u2800\u2800\u2800\u2800",
|
|
5454
|
-
"\u2800\u2800\u28E0\u28FF\u28FF\u2800\u2800\u2800",
|
|
5455
|
-
"\u2800\u28FC\u28FF\u28FF\u28FF\u28C0\u28C0\u2840",
|
|
5456
|
-
"\u2808\u2809\u2809\u28FF\u28FF\u28FF\u285F\u2800",
|
|
5457
|
-
"\u2800\u2800\u2800\u28FF\u28FF\u280B\u2800\u2800",
|
|
5458
|
-
"\u2800\u2800\u2800\u287F\u2801\u2800\u2800\u2800"
|
|
5459
|
-
];
|
|
5460
|
-
var boltFillOrder = boltRows.flatMap(
|
|
5461
|
-
(row, rowIndex) => [...row].flatMap(
|
|
5462
|
-
(cell, columnIndex) => cell === BRAILLE_BLANK ? [] : rowIndex * row.length + columnIndex
|
|
5463
|
-
)
|
|
5464
|
-
);
|
|
5465
|
-
var fillStages = [
|
|
5466
|
-
...Array.from({ length: boltFillOrder.length + 1 }, (_, index) => index),
|
|
5467
|
-
boltFillOrder.length,
|
|
5468
|
-
0
|
|
5469
|
-
];
|
|
5470
|
-
var boltFillRanks = new Map(
|
|
5471
|
-
boltFillOrder.map((cellIndex, fillIndex) => [cellIndex, fillIndex])
|
|
5472
|
-
);
|
|
5473
|
-
var STATUS_ROW_INDEX = Math.floor(boltRows.length / 2);
|
|
5474
|
-
function formatPhase({ label, detail }) {
|
|
5475
|
-
return detail ? `${chalk5.bold(label)} ${chalk5.dim(detail)}` : chalk5.bold(label);
|
|
5476
|
-
}
|
|
5477
|
-
function formatLoaderFrame({
|
|
5478
|
-
phase,
|
|
5479
|
-
fillStage
|
|
5480
|
-
}) {
|
|
5481
|
-
return boltRows.map((row, rowIndex) => {
|
|
5482
|
-
const bolt = [...row].map((cell, columnIndex) => {
|
|
5483
|
-
if (cell === BRAILLE_BLANK) return cell;
|
|
5484
|
-
const cellIndex = rowIndex * row.length + columnIndex;
|
|
5485
|
-
const fillRank = boltFillRanks.get(cellIndex);
|
|
5486
|
-
const isFilled = fillRank !== void 0 && fillRank < fillStage;
|
|
5487
|
-
return isFilled ? chalk5.bold.yellow(cell) : chalk5.dim.yellow(cell);
|
|
5488
|
-
}).join("");
|
|
5489
|
-
const status = rowIndex === STATUS_ROW_INDEX ? ` ${formatPhase(phase)}` : "";
|
|
5490
|
-
return `${bolt}${status}`;
|
|
5491
|
-
}).join("\n");
|
|
5492
|
-
}
|
|
5493
|
-
async function runWithSetupLoader({
|
|
5494
|
-
promise,
|
|
5495
|
-
...phase
|
|
5496
|
-
}) {
|
|
5497
|
-
const startedAt = Date.now();
|
|
5498
|
-
let fillFrameIndex = 0;
|
|
5499
|
-
const loader = ora({
|
|
5500
|
-
isEnabled: process.stderr.isTTY === true,
|
|
5501
|
-
spinner: { interval: 80, frames: [""] },
|
|
5502
|
-
text: formatLoaderFrame({
|
|
5503
|
-
phase,
|
|
5504
|
-
fillStage: fillStages[fillFrameIndex]
|
|
5505
|
-
})
|
|
5506
|
-
}).start();
|
|
5507
|
-
const animationTimer = setInterval(() => {
|
|
5508
|
-
fillFrameIndex = (fillFrameIndex + 1) % fillStages.length;
|
|
5509
|
-
loader.text = formatLoaderFrame({
|
|
5510
|
-
phase,
|
|
5511
|
-
fillStage: fillStages[fillFrameIndex]
|
|
5512
|
-
});
|
|
5513
|
-
}, LIGHTNING_INTERVAL_MILLISECONDS);
|
|
5514
|
-
animationTimer.unref?.();
|
|
5515
|
-
try {
|
|
5516
|
-
const result = await promise;
|
|
5517
|
-
clearInterval(animationTimer);
|
|
5518
|
-
const elapsedSeconds = ((Date.now() - startedAt) / 1e3).toFixed(1);
|
|
5519
|
-
loader.succeed(`${formatPhase(phase)} ${chalk5.dim(`${elapsedSeconds}s`)}`);
|
|
5520
|
-
return result;
|
|
5521
|
-
} catch (error) {
|
|
5522
|
-
clearInterval(animationTimer);
|
|
5523
|
-
const elapsedSeconds = ((Date.now() - startedAt) / 1e3).toFixed(1);
|
|
5524
|
-
loader.fail(`${formatPhase(phase)} ${chalk5.dim(`${elapsedSeconds}s`)}`);
|
|
5525
|
-
throw error;
|
|
5526
|
-
}
|
|
5527
|
-
}
|
|
5528
5471
|
function isRecord(value) {
|
|
5529
5472
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
5530
5473
|
}
|
|
@@ -5810,7 +5753,7 @@ async function offerPackageUpdate({
|
|
|
5810
5753
|
}) {
|
|
5811
5754
|
const { currentVersion, dependency } = packageState;
|
|
5812
5755
|
if (!currentVersion || !dependency.checkForUpdates) return;
|
|
5813
|
-
const update = await
|
|
5756
|
+
const update = await context.load({
|
|
5814
5757
|
promise: context.checkForUpdates({
|
|
5815
5758
|
packageName: dependency.name,
|
|
5816
5759
|
currentVersion
|
|
@@ -5835,28 +5778,108 @@ async function offerPackageUpdate({
|
|
|
5835
5778
|
})
|
|
5836
5779
|
);
|
|
5837
5780
|
}
|
|
5838
|
-
|
|
5781
|
+
function getPackageStates(context) {
|
|
5839
5782
|
const declaredPackageNames = getDeclaredPackageNames(context.cwd);
|
|
5840
5783
|
const requiredPackages = REQUIRED_PACKAGES.filter(
|
|
5841
5784
|
({ typescriptOnly }) => !typescriptOnly || context.projectLanguage === "typescript"
|
|
5842
5785
|
);
|
|
5843
|
-
|
|
5786
|
+
return requiredPackages.map((dependency) => ({
|
|
5844
5787
|
dependency,
|
|
5788
|
+
declared: declaredPackageNames.has(dependency.name),
|
|
5845
5789
|
currentVersion: declaredPackageNames.has(dependency.name) ? getInstalledPackageVersion({
|
|
5846
5790
|
cwd: context.cwd,
|
|
5847
5791
|
packageName: dependency.name
|
|
5848
5792
|
}) : void 0
|
|
5849
5793
|
}));
|
|
5794
|
+
}
|
|
5795
|
+
async function prepareDependencies(context) {
|
|
5796
|
+
const packages = getPackageStates(context);
|
|
5850
5797
|
await offerMissingPackageInstall({
|
|
5851
5798
|
context,
|
|
5852
|
-
packages: packages.filter(
|
|
5853
|
-
({ dependency }) => !declaredPackageNames.has(dependency.name)
|
|
5854
|
-
)
|
|
5799
|
+
packages: packages.filter(({ declared }) => !declared)
|
|
5855
5800
|
});
|
|
5856
5801
|
for (const packageState of packages) {
|
|
5857
5802
|
await offerPackageUpdate({ context, packageState });
|
|
5858
5803
|
}
|
|
5859
5804
|
}
|
|
5805
|
+
var BRAILLE_BLANK = "\u2800";
|
|
5806
|
+
var LIGHTNING_INTERVAL_MILLISECONDS = 140;
|
|
5807
|
+
var boltRows = [
|
|
5808
|
+
"\u2800\u2800\u2880\u28FE\u2800\u2800\u2800\u2800",
|
|
5809
|
+
"\u2800\u2800\u28E0\u28FF\u28FF\u2800\u2800\u2800",
|
|
5810
|
+
"\u2800\u28FC\u28FF\u28FF\u28FF\u28C0\u28C0\u2840",
|
|
5811
|
+
"\u2808\u2809\u2809\u28FF\u28FF\u28FF\u285F\u2800",
|
|
5812
|
+
"\u2800\u2800\u2800\u28FF\u28FF\u280B\u2800\u2800",
|
|
5813
|
+
"\u2800\u2800\u2800\u287F\u2801\u2800\u2800\u2800"
|
|
5814
|
+
];
|
|
5815
|
+
var boltFillOrder = boltRows.flatMap(
|
|
5816
|
+
(row, rowIndex) => [...row].flatMap(
|
|
5817
|
+
(cell, columnIndex) => cell === BRAILLE_BLANK ? [] : rowIndex * row.length + columnIndex
|
|
5818
|
+
)
|
|
5819
|
+
);
|
|
5820
|
+
var fillStages = [
|
|
5821
|
+
...Array.from({ length: boltFillOrder.length + 1 }, (_, index) => index),
|
|
5822
|
+
boltFillOrder.length,
|
|
5823
|
+
0
|
|
5824
|
+
];
|
|
5825
|
+
var boltFillRanks = new Map(
|
|
5826
|
+
boltFillOrder.map((cellIndex, fillIndex) => [cellIndex, fillIndex])
|
|
5827
|
+
);
|
|
5828
|
+
var STATUS_ROW_INDEX = Math.floor(boltRows.length / 2);
|
|
5829
|
+
function formatPhase({ label, detail }) {
|
|
5830
|
+
return detail ? `${chalk5.bold(label)} ${chalk5.dim(detail)}` : chalk5.bold(label);
|
|
5831
|
+
}
|
|
5832
|
+
function formatLoaderFrame({
|
|
5833
|
+
phase,
|
|
5834
|
+
fillStage
|
|
5835
|
+
}) {
|
|
5836
|
+
return boltRows.map((row, rowIndex) => {
|
|
5837
|
+
const bolt = [...row].map((cell, columnIndex) => {
|
|
5838
|
+
if (cell === BRAILLE_BLANK) return cell;
|
|
5839
|
+
const cellIndex = rowIndex * row.length + columnIndex;
|
|
5840
|
+
const fillRank = boltFillRanks.get(cellIndex);
|
|
5841
|
+
const isFilled = fillRank !== void 0 && fillRank < fillStage;
|
|
5842
|
+
return isFilled ? chalk5.bold.yellow(cell) : chalk5.dim.yellow(cell);
|
|
5843
|
+
}).join("");
|
|
5844
|
+
const status = rowIndex === STATUS_ROW_INDEX ? ` ${formatPhase(phase)}` : "";
|
|
5845
|
+
return `${bolt}${status}`;
|
|
5846
|
+
}).join("\n");
|
|
5847
|
+
}
|
|
5848
|
+
async function runWithSetupLoader({
|
|
5849
|
+
promise,
|
|
5850
|
+
...phase
|
|
5851
|
+
}) {
|
|
5852
|
+
const startedAt = Date.now();
|
|
5853
|
+
let fillFrameIndex = 0;
|
|
5854
|
+
const loader = ora({
|
|
5855
|
+
isEnabled: process.stderr.isTTY === true,
|
|
5856
|
+
spinner: { interval: 80, frames: [""] },
|
|
5857
|
+
text: formatLoaderFrame({
|
|
5858
|
+
phase,
|
|
5859
|
+
fillStage: fillStages[fillFrameIndex]
|
|
5860
|
+
})
|
|
5861
|
+
}).start();
|
|
5862
|
+
const animationTimer = setInterval(() => {
|
|
5863
|
+
fillFrameIndex = (fillFrameIndex + 1) % fillStages.length;
|
|
5864
|
+
loader.text = formatLoaderFrame({
|
|
5865
|
+
phase,
|
|
5866
|
+
fillStage: fillStages[fillFrameIndex]
|
|
5867
|
+
});
|
|
5868
|
+
}, LIGHTNING_INTERVAL_MILLISECONDS);
|
|
5869
|
+
animationTimer.unref?.();
|
|
5870
|
+
try {
|
|
5871
|
+
const result = await promise;
|
|
5872
|
+
clearInterval(animationTimer);
|
|
5873
|
+
const elapsedSeconds = ((Date.now() - startedAt) / 1e3).toFixed(1);
|
|
5874
|
+
loader.succeed(`${formatPhase(phase)} ${chalk5.dim(`${elapsedSeconds}s`)}`);
|
|
5875
|
+
return result;
|
|
5876
|
+
} catch (error) {
|
|
5877
|
+
clearInterval(animationTimer);
|
|
5878
|
+
const elapsedSeconds = ((Date.now() - startedAt) / 1e3).toFixed(1);
|
|
5879
|
+
loader.fail(`${formatPhase(phase)} ${chalk5.dim(`${elapsedSeconds}s`)}`);
|
|
5880
|
+
throw error;
|
|
5881
|
+
}
|
|
5882
|
+
}
|
|
5860
5883
|
|
|
5861
5884
|
// src/plugins/setup/package-commands.ts
|
|
5862
5885
|
function createLocalPackageCommand({
|
|
@@ -5895,7 +5918,10 @@ function formatPackageCommand({
|
|
|
5895
5918
|
command,
|
|
5896
5919
|
args
|
|
5897
5920
|
}) {
|
|
5898
|
-
return [command, ...args].join(" ");
|
|
5921
|
+
return [command, ...args].map(quoteShellArgument).join(" ");
|
|
5922
|
+
}
|
|
5923
|
+
function quoteShellArgument(value) {
|
|
5924
|
+
return /^[a-zA-Z0-9_@%+=:,./-]+$/.test(value) ? value : `'${value.replace(/'/g, `'"'"'`)}'`;
|
|
5899
5925
|
}
|
|
5900
5926
|
|
|
5901
5927
|
// src/plugins/setup/project.ts
|
|
@@ -6000,13 +6026,14 @@ async function runCommand({
|
|
|
6000
6026
|
args = [],
|
|
6001
6027
|
cwd,
|
|
6002
6028
|
capture = true,
|
|
6003
|
-
input
|
|
6029
|
+
input,
|
|
6030
|
+
load = runWithSetupLoader
|
|
6004
6031
|
}) {
|
|
6005
6032
|
const commandLabel = `$ ${command} ${args.join(" ")}`.trim();
|
|
6006
6033
|
const execute = capture ? executeCapturedCommand({ command, args, cwd, input }) : executeStreamingCommand({ command, args, cwd, input });
|
|
6007
6034
|
try {
|
|
6008
6035
|
if (capture) {
|
|
6009
|
-
return await
|
|
6036
|
+
return await load({
|
|
6010
6037
|
promise: execute,
|
|
6011
6038
|
label: "Running command",
|
|
6012
6039
|
detail: commandLabel
|
|
@@ -6090,7 +6117,7 @@ function detectProjectLanguage({
|
|
|
6090
6117
|
);
|
|
6091
6118
|
return hasTypeScript ? "typescript" : "javascript";
|
|
6092
6119
|
}
|
|
6093
|
-
async function
|
|
6120
|
+
async function initializeProject(context) {
|
|
6094
6121
|
context.packageManager = detectPackageManager(context.cwd).name;
|
|
6095
6122
|
console.log(`Using ${context.packageManager} for dependency installs.`);
|
|
6096
6123
|
const hasExistingProject = existsSync(join(context.cwd, "package.json"));
|
|
@@ -6106,182 +6133,161 @@ async function prepareProject(context) {
|
|
|
6106
6133
|
cwd: context.cwd
|
|
6107
6134
|
});
|
|
6108
6135
|
}
|
|
6136
|
+
}
|
|
6137
|
+
async function prepareProject(context) {
|
|
6138
|
+
await initializeProject(context);
|
|
6109
6139
|
await prepareDependencies(context);
|
|
6110
6140
|
}
|
|
6111
6141
|
|
|
6112
6142
|
// src/plugins/setup/context.ts
|
|
6113
6143
|
function createWizardContext({
|
|
6114
|
-
imports
|
|
6144
|
+
imports,
|
|
6145
|
+
agent = false
|
|
6115
6146
|
}) {
|
|
6116
|
-
|
|
6147
|
+
const context = {
|
|
6117
6148
|
cwd: process.cwd(),
|
|
6149
|
+
load: agent ? ({ promise }) => promise : runWithSetupLoader,
|
|
6118
6150
|
imports,
|
|
6119
6151
|
createAnswer: () => createCliAnswer({ mode: "closed-list" }),
|
|
6120
6152
|
setupController: createSetupController(),
|
|
6121
|
-
selectStoredLoginCredentials: () => chooseStoredLoginCredentials({
|
|
6122
|
-
|
|
6153
|
+
selectStoredLoginCredentials: () => chooseStoredLoginCredentials({
|
|
6154
|
+
imports,
|
|
6155
|
+
options: {},
|
|
6156
|
+
...agent ? {
|
|
6157
|
+
prompt: async (options) => {
|
|
6158
|
+
const credentialChoices = buildCredentialsChoices(options);
|
|
6159
|
+
const choices = credentialChoices.map(({ name, value }) => ({
|
|
6160
|
+
label: name,
|
|
6161
|
+
value: value.action === "create" ? "create" : `account:${value.credentials.name}`,
|
|
6162
|
+
result: value
|
|
6163
|
+
}));
|
|
6164
|
+
const selected = await resolveSetupSelect({
|
|
6165
|
+
answer: context.createAnswer(),
|
|
6166
|
+
controller: context.setupController,
|
|
6167
|
+
message: CREDENTIALS_PICKER_QUESTION,
|
|
6168
|
+
choices
|
|
6169
|
+
});
|
|
6170
|
+
const selectedIndex = choices.findIndex(
|
|
6171
|
+
({ value }) => value === selected
|
|
6172
|
+
);
|
|
6173
|
+
return choices[selectedIndex].result;
|
|
6174
|
+
}
|
|
6175
|
+
} : {}
|
|
6176
|
+
}),
|
|
6177
|
+
authenticateNewAccount: ({ entryPoint, ...options }) => runAccountAuth({
|
|
6123
6178
|
imports,
|
|
6124
|
-
options
|
|
6179
|
+
options,
|
|
6125
6180
|
entryPoint,
|
|
6126
6181
|
storedCredentialsMode: "fresh",
|
|
6127
|
-
preserveExistingCredentials: true
|
|
6182
|
+
preserveExistingCredentials: true,
|
|
6183
|
+
useBrowserCallback: agent && options.headless === false,
|
|
6184
|
+
silent: agent && options.headless
|
|
6128
6185
|
}),
|
|
6129
6186
|
checkForUpdates,
|
|
6130
6187
|
openUrl: async (url) => {
|
|
6131
6188
|
await open(url);
|
|
6132
6189
|
},
|
|
6133
|
-
runCommand
|
|
6190
|
+
runCommand: agent ? (options) => runCommand({
|
|
6191
|
+
...options,
|
|
6192
|
+
capture: true,
|
|
6193
|
+
load: ({ promise, detail }) => {
|
|
6194
|
+
console.log(detail);
|
|
6195
|
+
return promise;
|
|
6196
|
+
}
|
|
6197
|
+
}) : runCommand,
|
|
6134
6198
|
packageManager: detectPackageManager(process.cwd()).name,
|
|
6135
6199
|
projectLanguage: "typescript"
|
|
6136
6200
|
};
|
|
6201
|
+
return context;
|
|
6202
|
+
}
|
|
6203
|
+
var SetupStepSchema = z.enum([
|
|
6204
|
+
"directory",
|
|
6205
|
+
"project",
|
|
6206
|
+
"install",
|
|
6207
|
+
"sdk-update",
|
|
6208
|
+
"cli-update",
|
|
6209
|
+
"skill",
|
|
6210
|
+
"account",
|
|
6211
|
+
"account-flow",
|
|
6212
|
+
"account-environment",
|
|
6213
|
+
"authorize",
|
|
6214
|
+
"confirm-account",
|
|
6215
|
+
"connections",
|
|
6216
|
+
"slack",
|
|
6217
|
+
"slack-overwrite",
|
|
6218
|
+
"slack-existing",
|
|
6219
|
+
"slack-retry",
|
|
6220
|
+
"handoff"
|
|
6221
|
+
]);
|
|
6222
|
+
var SetupSchema = z.object({
|
|
6223
|
+
agent: z.boolean().optional().describe(
|
|
6224
|
+
"For AI agents and callers without a terminal: print the next setup question and the exact command to run for each answer."
|
|
6225
|
+
),
|
|
6226
|
+
step: SetupStepSchema.optional().describe(
|
|
6227
|
+
"Setup operation to inspect or execute."
|
|
6228
|
+
),
|
|
6229
|
+
nonInteractive: z.boolean().optional().describe("Execute the named setup step without terminal prompts."),
|
|
6230
|
+
answer: z.string().optional().describe(
|
|
6231
|
+
"User selection or input for a non-interactive setup step. Omitted confirmations use the terminal wizard's default."
|
|
6232
|
+
),
|
|
6233
|
+
flow: z.enum(["login", "signup"]).optional().describe(
|
|
6234
|
+
"Account flow selected for the environment and OAuth callback steps."
|
|
6235
|
+
),
|
|
6236
|
+
language: z.enum(["javascript", "typescript"]).optional().describe(
|
|
6237
|
+
"Project language for dependency installation and the Slack demo."
|
|
6238
|
+
)
|
|
6239
|
+
}).describe(
|
|
6240
|
+
"Set up the Zapier SDK in an existing or new project. Runs an interactive terminal wizard; AI agents should use --agent instead."
|
|
6241
|
+
);
|
|
6242
|
+
|
|
6243
|
+
// package.json
|
|
6244
|
+
var package_default = {
|
|
6245
|
+
version: "0.87.1"};
|
|
6246
|
+
var agentLabels = {
|
|
6247
|
+
claude: "Claude Code",
|
|
6248
|
+
codex: "Codex",
|
|
6249
|
+
cursor: "Cursor",
|
|
6250
|
+
"vscode-copilot": "VS Code Copilot"
|
|
6251
|
+
};
|
|
6252
|
+
function buildAgentPrompt({
|
|
6253
|
+
context
|
|
6254
|
+
}) {
|
|
6255
|
+
const listConnectionsCommand = formatPackageCommand(
|
|
6256
|
+
createLocalPackageCommand({
|
|
6257
|
+
packageManager: context.packageManager,
|
|
6258
|
+
binary: "zapier-sdk",
|
|
6259
|
+
args: ["list-connections", "--owner", "me", "--json"]
|
|
6260
|
+
})
|
|
6261
|
+
);
|
|
6262
|
+
const listActionsCommand = formatPackageCommand(
|
|
6263
|
+
createLocalPackageCommand({
|
|
6264
|
+
packageManager: context.packageManager,
|
|
6265
|
+
binary: "zapier-sdk",
|
|
6266
|
+
args: ["list-actions", "APP_KEY"]
|
|
6267
|
+
})
|
|
6268
|
+
);
|
|
6269
|
+
return `Start by running this command to discover my active connected apps outside of your sandbox:
|
|
6270
|
+
|
|
6271
|
+
${listConnectionsCommand}
|
|
6272
|
+
|
|
6273
|
+
Using the connected apps returned by that command:
|
|
6274
|
+
|
|
6275
|
+
- Pick 1-3 apps and show commands I can use to explore their actions, such as: ${listActionsCommand}
|
|
6276
|
+
- Do not run those list-actions commands.
|
|
6277
|
+
- Suggest 3 specific ideas based on my connected apps. Each idea should read from one app, optionally process data, and write to another.
|
|
6278
|
+
- Do not suggest event-driven workflows.
|
|
6279
|
+
- Keep each idea to one sentence. Example: "pull in-progress Jira issues and DM yourself a summary on Slack".
|
|
6280
|
+
|
|
6281
|
+
Do not inspect files or modify the project. If fewer than two distinct apps are listed, explain that cross-app ideas require another active connection instead of inventing apps.`;
|
|
6137
6282
|
}
|
|
6138
|
-
|
|
6139
|
-
function printAuthCommand({
|
|
6283
|
+
function printAgentPrompt({
|
|
6140
6284
|
context,
|
|
6141
|
-
|
|
6142
|
-
headless
|
|
6285
|
+
message
|
|
6143
6286
|
}) {
|
|
6144
|
-
|
|
6145
|
-
|
|
6146
|
-
|
|
6147
|
-
|
|
6148
|
-
});
|
|
6149
|
-
console.log(`$ ${formatPackageCommand(command)}`);
|
|
6150
|
-
if (headless) {
|
|
6151
|
-
console.log(
|
|
6152
|
-
"Open the printed URL in another browser, then paste the final OAuth callback URL when prompted."
|
|
6153
|
-
);
|
|
6154
|
-
}
|
|
6155
|
-
}
|
|
6156
|
-
function isCredentialWriteError(error) {
|
|
6157
|
-
const code = typeof error === "object" && error !== null && "code" in error && typeof error.code === "string" ? error.code : "";
|
|
6158
|
-
if (/^(eacces|eperm)$/i.test(code)) return true;
|
|
6159
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
6160
|
-
return /permission|sandbox|eacces|eperm/i.test(message);
|
|
6161
|
-
}
|
|
6162
|
-
async function confirmAuthentication(context) {
|
|
6163
|
-
const { data: profile } = await runWithSetupLoader({
|
|
6164
|
-
promise: context.imports.getProfile({}),
|
|
6165
|
-
label: "Confirming Zapier account"
|
|
6166
|
-
});
|
|
6167
|
-
context.accountEmail = profile.email;
|
|
6168
|
-
console.log(`Authenticated as ${profile.email}.`);
|
|
6169
|
-
}
|
|
6170
|
-
async function authenticate(context) {
|
|
6171
|
-
const activeProfile = await runWithSetupLoader({
|
|
6172
|
-
promise: context.imports.getProfile({}).then(({ data }) => data).catch((error) => {
|
|
6173
|
-
if (isZapierAuthenticationError(error)) return void 0;
|
|
6174
|
-
throw error;
|
|
6175
|
-
}),
|
|
6176
|
-
label: "Checking Zapier authentication"
|
|
6177
|
-
});
|
|
6178
|
-
const storedCredentialsSelection = await context.selectStoredLoginCredentials();
|
|
6179
|
-
if (storedCredentialsSelection === "switch") {
|
|
6180
|
-
await confirmAuthentication(context);
|
|
6181
|
-
return;
|
|
6182
|
-
}
|
|
6183
|
-
if (storedCredentialsSelection === "none" && activeProfile) {
|
|
6184
|
-
const shouldLogout = await resolveSetupConfirm({
|
|
6185
|
-
answer: context.createAnswer(),
|
|
6186
|
-
controller: context.setupController,
|
|
6187
|
-
message: `You are already logged in as "${activeProfile.email}".
|
|
6188
|
-
Logging out will delete these credentials and may interrupt other Zapier SDK or CLI sessions using them.
|
|
6189
|
-
Log out and use a different account?`,
|
|
6190
|
-
defaultValue: false
|
|
6191
|
-
});
|
|
6192
|
-
if (!shouldLogout) {
|
|
6193
|
-
context.accountEmail = activeProfile.email;
|
|
6194
|
-
console.log(`Continuing as ${activeProfile.email}.`);
|
|
6195
|
-
return;
|
|
6196
|
-
}
|
|
6197
|
-
const logoutCommand = createLocalPackageCommand({
|
|
6198
|
-
packageManager: context.packageManager,
|
|
6199
|
-
binary: "zapier-sdk",
|
|
6200
|
-
args: ["logout"]
|
|
6201
|
-
});
|
|
6202
|
-
console.log(`$ ${formatPackageCommand(logoutCommand)}`);
|
|
6203
|
-
await context.imports.logout({});
|
|
6204
|
-
}
|
|
6205
|
-
const flow = await resolveSetupSelect({
|
|
6206
|
-
answer: context.createAnswer(),
|
|
6207
|
-
controller: context.setupController,
|
|
6208
|
-
message: "Zapier account:",
|
|
6209
|
-
choices: [
|
|
6210
|
-
{ label: "Log in to an existing account", value: "login" },
|
|
6211
|
-
{ label: "Create a new Zapier account", value: "signup" }
|
|
6212
|
-
]
|
|
6213
|
-
});
|
|
6214
|
-
const environment = await resolveSetupSelect({
|
|
6215
|
-
answer: context.createAnswer(),
|
|
6216
|
-
controller: context.setupController,
|
|
6217
|
-
message: "Environment:",
|
|
6218
|
-
choices: [
|
|
6219
|
-
{ label: "Local machine with a browser", value: "local" },
|
|
6220
|
-
{ label: "Remote or headless", value: "headless" }
|
|
6221
|
-
]
|
|
6222
|
-
});
|
|
6223
|
-
const headless = environment === "headless";
|
|
6224
|
-
printAuthCommand({ context, flow, headless });
|
|
6225
|
-
try {
|
|
6226
|
-
if (storedCredentialsSelection === "create") {
|
|
6227
|
-
await context.authenticateNewAccount({ entryPoint: flow, headless });
|
|
6228
|
-
} else {
|
|
6229
|
-
const runAuth = flow === "login" ? context.imports.login : context.imports.signup;
|
|
6230
|
-
await runAuth({ headless });
|
|
6231
|
-
}
|
|
6232
|
-
} catch (error) {
|
|
6233
|
-
if (isCredentialWriteError(error)) {
|
|
6234
|
-
console.error(
|
|
6235
|
-
"Authentication could not write credentials. A permissions or sandbox restriction may be blocking disk access."
|
|
6236
|
-
);
|
|
6237
|
-
}
|
|
6238
|
-
throw error;
|
|
6239
|
-
}
|
|
6240
|
-
await confirmAuthentication(context);
|
|
6241
|
-
}
|
|
6242
|
-
var agentLabels = {
|
|
6243
|
-
claude: "Claude Code",
|
|
6244
|
-
codex: "Codex",
|
|
6245
|
-
cursor: "Cursor",
|
|
6246
|
-
"vscode-copilot": "VS Code Copilot"
|
|
6247
|
-
};
|
|
6248
|
-
function buildAgentPrompt({ context }) {
|
|
6249
|
-
const listConnectionsCommand = formatPackageCommand(
|
|
6250
|
-
createLocalPackageCommand({
|
|
6251
|
-
packageManager: context.packageManager,
|
|
6252
|
-
binary: "zapier-sdk",
|
|
6253
|
-
args: ["list-connections", "--owner", "me", "--json"]
|
|
6254
|
-
})
|
|
6255
|
-
);
|
|
6256
|
-
const listActionsCommand = formatPackageCommand(
|
|
6257
|
-
createLocalPackageCommand({
|
|
6258
|
-
packageManager: context.packageManager,
|
|
6259
|
-
binary: "zapier-sdk",
|
|
6260
|
-
args: ["list-actions", "APP_KEY"]
|
|
6261
|
-
})
|
|
6262
|
-
);
|
|
6263
|
-
return `Start by running this command to discover my active connected apps outside of your sandbox:
|
|
6264
|
-
|
|
6265
|
-
${listConnectionsCommand}
|
|
6266
|
-
|
|
6267
|
-
Using the connected apps returned by that command:
|
|
6268
|
-
|
|
6269
|
-
- Pick 1-3 apps and show commands I can use to explore their actions, such as: ${listActionsCommand}
|
|
6270
|
-
- Do not run those list-actions commands.
|
|
6271
|
-
- Suggest 3 specific ideas based on my connected apps. Each idea should read from one app, optionally process data, and write to another.
|
|
6272
|
-
- Do not suggest event-driven workflows.
|
|
6273
|
-
- Keep each idea to one sentence. Example: "pull in-progress Jira issues and DM yourself a summary on Slack".
|
|
6274
|
-
|
|
6275
|
-
Do not inspect files or modify the project. If fewer than two distinct apps are listed, explain that cross-app ideas require another active connection instead of inventing apps.`;
|
|
6276
|
-
}
|
|
6277
|
-
function printAgentPrompt({
|
|
6278
|
-
context,
|
|
6279
|
-
message
|
|
6280
|
-
}) {
|
|
6281
|
-
console.log();
|
|
6282
|
-
console.log(chalk5.bgYellow.black.bold(message));
|
|
6283
|
-
console.log();
|
|
6284
|
-
console.log(buildAgentPrompt({ context }));
|
|
6287
|
+
console.log();
|
|
6288
|
+
console.log(chalk5.bgYellow.black.bold(message));
|
|
6289
|
+
console.log();
|
|
6290
|
+
console.log(buildAgentPrompt({ context }));
|
|
6285
6291
|
}
|
|
6286
6292
|
async function openAgentHandoff(context) {
|
|
6287
6293
|
const agent = await resolveSetupSelect({
|
|
@@ -6352,27 +6358,291 @@ async function openAgentHandoff(context) {
|
|
|
6352
6358
|
message: "If it didn't load correctly, paste this prompt into your agent:"
|
|
6353
6359
|
});
|
|
6354
6360
|
}
|
|
6361
|
+
|
|
6362
|
+
// src/plugins/setup/agent-output.ts
|
|
6363
|
+
var REQUIRED_STEPS = [
|
|
6364
|
+
"directory",
|
|
6365
|
+
"install"
|
|
6366
|
+
];
|
|
6367
|
+
function formatSetupCommand({
|
|
6368
|
+
context,
|
|
6369
|
+
target,
|
|
6370
|
+
answer
|
|
6371
|
+
}) {
|
|
6372
|
+
const args = [
|
|
6373
|
+
"setup",
|
|
6374
|
+
"--step",
|
|
6375
|
+
target.step,
|
|
6376
|
+
"--non-interactive",
|
|
6377
|
+
...target.flow ? ["--flow", target.flow] : [],
|
|
6378
|
+
"--language",
|
|
6379
|
+
context.projectLanguage,
|
|
6380
|
+
...answer !== void 0 ? ["--answer", answer] : []
|
|
6381
|
+
];
|
|
6382
|
+
return formatPackageCommand(
|
|
6383
|
+
SOURCE_CLI_PATH ? {
|
|
6384
|
+
command: process.execPath,
|
|
6385
|
+
args: [
|
|
6386
|
+
"--import",
|
|
6387
|
+
createRequire(import.meta.url).resolve("tsx"),
|
|
6388
|
+
"--conditions",
|
|
6389
|
+
"source",
|
|
6390
|
+
SOURCE_CLI_PATH,
|
|
6391
|
+
...args
|
|
6392
|
+
]
|
|
6393
|
+
} : createPackageRunnerCommand({
|
|
6394
|
+
packageManager: context.packageManager,
|
|
6395
|
+
packageName: `@zapier/zapier-sdk-cli@${package_default.version}`,
|
|
6396
|
+
args
|
|
6397
|
+
})
|
|
6398
|
+
);
|
|
6399
|
+
}
|
|
6400
|
+
function printAgentStep({
|
|
6401
|
+
context,
|
|
6402
|
+
target,
|
|
6403
|
+
question
|
|
6404
|
+
}) {
|
|
6405
|
+
console.log(
|
|
6406
|
+
`Step: ${target.step}
|
|
6407
|
+
Working directory: ${context.cwd}
|
|
6408
|
+
Detected package manager: ${context.packageManager}
|
|
6409
|
+
Project language: ${context.projectLanguage}`
|
|
6410
|
+
);
|
|
6411
|
+
if (!question) {
|
|
6412
|
+
console.log(
|
|
6413
|
+
`Run this exact command:
|
|
6414
|
+
${formatSetupCommand({ context, target })}`
|
|
6415
|
+
);
|
|
6416
|
+
} else {
|
|
6417
|
+
if (target.step === "authorize" && context.authorizationUrl)
|
|
6418
|
+
console.log(
|
|
6419
|
+
`Give the user this URL to open in a browser where they can sign in to Zapier:
|
|
6420
|
+
${context.authorizationUrl}
|
|
6421
|
+
After they approve, the browser opens a localhost page that may fail to load. That is expected: ask them to copy the full address from the browser's address bar. Pass that address only to the command below; do not save it or repeat it elsewhere.`
|
|
6422
|
+
);
|
|
6423
|
+
console.log(`Ask the user this question verbatim: "${question.message}"`);
|
|
6424
|
+
if (question.type === "select") {
|
|
6425
|
+
for (const { value, label } of question.choices)
|
|
6426
|
+
console.log(`${JSON.stringify(value)}: ${label}`);
|
|
6427
|
+
for (const { value } of question.choices) {
|
|
6428
|
+
const decision = value === "true" ? "agrees" : value === "false" ? "declines" : `chooses ${JSON.stringify(value)}`;
|
|
6429
|
+
if (value === "false" && REQUIRED_STEPS.includes(target.step))
|
|
6430
|
+
console.log("If the user declines, stop setup.");
|
|
6431
|
+
else
|
|
6432
|
+
console.log(
|
|
6433
|
+
`If the user ${decision}, run this exact command:
|
|
6434
|
+
${formatSetupCommand({ context, target, answer: value })}`
|
|
6435
|
+
);
|
|
6436
|
+
}
|
|
6437
|
+
} else {
|
|
6438
|
+
console.log(
|
|
6439
|
+
`Run this exact command with their answer as one shell-quoted argument (an empty answer is ''):
|
|
6440
|
+
${formatSetupCommand({ context, target, answer: "<answer>" })}`
|
|
6441
|
+
);
|
|
6442
|
+
}
|
|
6443
|
+
if (target.step === "account-environment")
|
|
6444
|
+
console.log(
|
|
6445
|
+
`The local command opens a browser and waits up to ${LOGIN_TIMEOUT_MILLISECONDS / 1e3} seconds for authorization, using the terminal login flow. Keep the process running until it completes and allow extra time for credential creation. If the browser does not open, use its printed fallback URL or choose headless instead.`
|
|
6446
|
+
);
|
|
6447
|
+
console.log(
|
|
6448
|
+
"Use your tool for asking the user questions if you have one. Do not answer on the user's behalf."
|
|
6449
|
+
);
|
|
6450
|
+
}
|
|
6451
|
+
console.log(
|
|
6452
|
+
"On success, follow the next step's guidance printed by that operation. On failure, stop and report the error; do not proceed or retry automatically. Ignore CLI update notices; setup offers package updates as their own steps."
|
|
6453
|
+
);
|
|
6454
|
+
}
|
|
6455
|
+
function printAgentCompletion({
|
|
6456
|
+
context,
|
|
6457
|
+
cancelled = false
|
|
6458
|
+
}) {
|
|
6459
|
+
console.log(
|
|
6460
|
+
cancelled ? "Setup cancelled." : `Setup walkthrough finished. Report which operations succeeded, failed, or were skipped based on their command results.
|
|
6461
|
+
Learn more: https://docs.zapier.com/sdk
|
|
6462
|
+
|
|
6463
|
+
Continue in this agent. Do not open another agent or ask the user to choose one.
|
|
6464
|
+
${buildAgentPrompt({ context })}`
|
|
6465
|
+
);
|
|
6466
|
+
}
|
|
6467
|
+
|
|
6468
|
+
// src/plugins/setup/authentication.ts
|
|
6469
|
+
function printAuthCommand({
|
|
6470
|
+
context,
|
|
6471
|
+
flow,
|
|
6472
|
+
headless = false
|
|
6473
|
+
}) {
|
|
6474
|
+
const command = createLocalPackageCommand({
|
|
6475
|
+
packageManager: context.packageManager,
|
|
6476
|
+
binary: "zapier-sdk",
|
|
6477
|
+
args: [flow, ...headless ? ["--headless"] : []]
|
|
6478
|
+
});
|
|
6479
|
+
console.log(`$ ${formatPackageCommand(command)}`);
|
|
6480
|
+
if (headless) {
|
|
6481
|
+
console.log(
|
|
6482
|
+
"Open the printed URL in another browser, then paste the final OAuth callback URL when prompted."
|
|
6483
|
+
);
|
|
6484
|
+
}
|
|
6485
|
+
}
|
|
6486
|
+
function isCredentialWriteError(error) {
|
|
6487
|
+
const code = typeof error === "object" && error !== null && "code" in error && typeof error.code === "string" ? error.code : "";
|
|
6488
|
+
if (/^(eacces|eperm)$/i.test(code)) return true;
|
|
6489
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
6490
|
+
return /permission|sandbox|eacces|eperm/i.test(message);
|
|
6491
|
+
}
|
|
6492
|
+
async function confirmAuthentication(context) {
|
|
6493
|
+
const { data: profile } = await context.load({
|
|
6494
|
+
promise: context.imports.getProfile({}),
|
|
6495
|
+
label: "Confirming Zapier account"
|
|
6496
|
+
});
|
|
6497
|
+
context.accountEmail = profile.email;
|
|
6498
|
+
console.log(`Authenticated as ${profile.email}.`);
|
|
6499
|
+
}
|
|
6500
|
+
function selectAccountFlow(context) {
|
|
6501
|
+
return resolveSetupSelect({
|
|
6502
|
+
answer: context.createAnswer(),
|
|
6503
|
+
controller: context.setupController,
|
|
6504
|
+
message: "Zapier account:",
|
|
6505
|
+
choices: [
|
|
6506
|
+
{ label: "Log in to an existing account", value: "login" },
|
|
6507
|
+
{ label: "Create a new Zapier account", value: "signup" }
|
|
6508
|
+
]
|
|
6509
|
+
});
|
|
6510
|
+
}
|
|
6511
|
+
function selectAuthenticationEnvironment(context) {
|
|
6512
|
+
return resolveSetupSelect({
|
|
6513
|
+
answer: context.createAnswer(),
|
|
6514
|
+
controller: context.setupController,
|
|
6515
|
+
message: "Environment:",
|
|
6516
|
+
choices: [
|
|
6517
|
+
{ label: "Local machine with a browser", value: "local" },
|
|
6518
|
+
{ label: "Remote or headless", value: "headless" }
|
|
6519
|
+
]
|
|
6520
|
+
});
|
|
6521
|
+
}
|
|
6522
|
+
async function selectAccount(context) {
|
|
6523
|
+
const activeProfile = await context.load({
|
|
6524
|
+
promise: context.imports.getProfile({}).then(({ data }) => data).catch((error) => {
|
|
6525
|
+
if (isZapierAuthenticationError(error)) return void 0;
|
|
6526
|
+
throw error;
|
|
6527
|
+
}),
|
|
6528
|
+
label: "Checking Zapier authentication"
|
|
6529
|
+
});
|
|
6530
|
+
const storedCredentialsSelection = await context.selectStoredLoginCredentials();
|
|
6531
|
+
if (storedCredentialsSelection === "none" && activeProfile) {
|
|
6532
|
+
const shouldLogout = await resolveSetupConfirm({
|
|
6533
|
+
answer: context.createAnswer(),
|
|
6534
|
+
controller: context.setupController,
|
|
6535
|
+
message: `You are already logged in as "${activeProfile.email}".
|
|
6536
|
+
Logging out will delete these credentials and may interrupt other Zapier SDK or CLI sessions using them.
|
|
6537
|
+
Log out and use a different account?`,
|
|
6538
|
+
defaultValue: false
|
|
6539
|
+
});
|
|
6540
|
+
if (!shouldLogout) {
|
|
6541
|
+
context.accountEmail = activeProfile.email;
|
|
6542
|
+
console.log(`Continuing as ${activeProfile.email}.`);
|
|
6543
|
+
return "continue";
|
|
6544
|
+
}
|
|
6545
|
+
printAuthCommand({ context, flow: "logout" });
|
|
6546
|
+
await context.imports.logout({});
|
|
6547
|
+
}
|
|
6548
|
+
return storedCredentialsSelection;
|
|
6549
|
+
}
|
|
6550
|
+
async function authenticate(context) {
|
|
6551
|
+
const next = await runAuthenticationStep({ context, options: {} });
|
|
6552
|
+
if (next.step === "confirm-account") await confirmAuthentication(context);
|
|
6553
|
+
}
|
|
6554
|
+
async function runAuthenticationStep({
|
|
6555
|
+
context,
|
|
6556
|
+
options
|
|
6557
|
+
}) {
|
|
6558
|
+
const { step } = options;
|
|
6559
|
+
let newAccount = step === "account-environment";
|
|
6560
|
+
if (!step || step === "account") {
|
|
6561
|
+
const selection = await selectAccount(context);
|
|
6562
|
+
if (selection === "switch") return { step: "confirm-account" };
|
|
6563
|
+
if (selection === "continue")
|
|
6564
|
+
return { step: step ? "confirm-account" : "connections" };
|
|
6565
|
+
if (step) return { step: "account-flow" };
|
|
6566
|
+
newAccount = selection === "create";
|
|
6567
|
+
}
|
|
6568
|
+
if (step === "confirm-account") {
|
|
6569
|
+
await confirmAuthentication(context);
|
|
6570
|
+
return { step: "connections" };
|
|
6571
|
+
}
|
|
6572
|
+
const flow = !step || step === "account-flow" ? await selectAccountFlow(context) : options.flow;
|
|
6573
|
+
if (step === "account-flow")
|
|
6574
|
+
return {
|
|
6575
|
+
step: "account-environment",
|
|
6576
|
+
flow
|
|
6577
|
+
};
|
|
6578
|
+
if (!flow)
|
|
6579
|
+
throw new ZapierCliValidationError(
|
|
6580
|
+
"This setup step requires --flow login or --flow signup."
|
|
6581
|
+
);
|
|
6582
|
+
let headless = true;
|
|
6583
|
+
let callbackUrl;
|
|
6584
|
+
let recoveryMessage;
|
|
6585
|
+
if (step === "authorize") {
|
|
6586
|
+
recoveryMessage = `Generate a fresh authorization URL with \`${formatSetupCommand(
|
|
6587
|
+
{
|
|
6588
|
+
context,
|
|
6589
|
+
target: { step: "account-environment", flow },
|
|
6590
|
+
answer: "headless"
|
|
6591
|
+
}
|
|
6592
|
+
)}\`.`;
|
|
6593
|
+
const pending = getPendingOauthFlow({ entryPoint: flow, recoveryMessage });
|
|
6594
|
+
context.authorizationUrl = pending.transaction.browserAuthUrl;
|
|
6595
|
+
callbackUrl = await resolveSetupInput({
|
|
6596
|
+
answer: context.createAnswer(),
|
|
6597
|
+
controller: context.setupController,
|
|
6598
|
+
message: OAUTH_CALLBACK_QUESTION
|
|
6599
|
+
});
|
|
6600
|
+
} else {
|
|
6601
|
+
headless = await selectAuthenticationEnvironment(context) === "headless";
|
|
6602
|
+
if (!step) printAuthCommand({ context, flow, headless });
|
|
6603
|
+
}
|
|
6604
|
+
const authOptions = {
|
|
6605
|
+
headless,
|
|
6606
|
+
...step ? { nonInteractive: true } : {},
|
|
6607
|
+
...callbackUrl !== void 0 ? { callbackUrl } : {}
|
|
6608
|
+
};
|
|
6609
|
+
try {
|
|
6610
|
+
if (newAccount) {
|
|
6611
|
+
await context.authenticateNewAccount({
|
|
6612
|
+
entryPoint: flow,
|
|
6613
|
+
...authOptions
|
|
6614
|
+
});
|
|
6615
|
+
} else {
|
|
6616
|
+
await context.imports[flow](authOptions);
|
|
6617
|
+
}
|
|
6618
|
+
} catch (error) {
|
|
6619
|
+
if (recoveryMessage) console.error(recoveryMessage);
|
|
6620
|
+
if (isCredentialWriteError(error)) {
|
|
6621
|
+
console.error(
|
|
6622
|
+
"Authentication could not write credentials. A permissions or sandbox restriction may be blocking disk access."
|
|
6623
|
+
);
|
|
6624
|
+
}
|
|
6625
|
+
throw error;
|
|
6626
|
+
}
|
|
6627
|
+
return step === "account-environment" && headless ? { step: "authorize", flow } : { step: "confirm-account" };
|
|
6628
|
+
}
|
|
6355
6629
|
var CONNECTIONS_URL = "https://zapier.com/app/assets/connections";
|
|
6356
6630
|
var CONNECTION_DISPLAY_LIMIT = 10;
|
|
6357
|
-
async function
|
|
6358
|
-
const page = await
|
|
6631
|
+
async function showActiveConnections(context) {
|
|
6632
|
+
const page = await context.load({
|
|
6359
6633
|
promise: context.imports.listConnections({
|
|
6360
6634
|
owner: "me",
|
|
6361
6635
|
maxItems: CONNECTION_DISPLAY_LIMIT
|
|
6362
6636
|
}),
|
|
6363
6637
|
label: "Loading app connections"
|
|
6364
6638
|
});
|
|
6365
|
-
if (page.data.length === 0)
|
|
6366
|
-
console.log(
|
|
6367
|
-
`No active connections found. Connect or reconnect an app at ${CONNECTIONS_URL}, then come back and run setup again.`
|
|
6368
|
-
);
|
|
6369
|
-
throw new ZapierCliExitError(
|
|
6370
|
-
"Setup stopped before completion because no active connections are available."
|
|
6371
|
-
);
|
|
6372
|
-
}
|
|
6639
|
+
if (page.data.length === 0) return false;
|
|
6373
6640
|
const table = new Table({
|
|
6374
6641
|
head: ["App", "Connection"],
|
|
6375
|
-
style: {
|
|
6642
|
+
style: {
|
|
6643
|
+
compact: true,
|
|
6644
|
+
...process.stdout.isTTY ? {} : { head: [], border: [] }
|
|
6645
|
+
}
|
|
6376
6646
|
});
|
|
6377
6647
|
table.push(
|
|
6378
6648
|
...page.data.map((connection) => [
|
|
@@ -6384,11 +6654,22 @@ async function ensureConnectionsAvailable(context) {
|
|
|
6384
6654
|
`Active Zapier connections (showing up to ${CONNECTION_DISPLAY_LIMIT}):`
|
|
6385
6655
|
);
|
|
6386
6656
|
console.log(table.toString());
|
|
6657
|
+
return true;
|
|
6658
|
+
}
|
|
6659
|
+
async function ensureConnectionsAvailable(context) {
|
|
6660
|
+
if (await showActiveConnections(context)) return;
|
|
6661
|
+
console.log(
|
|
6662
|
+
`No active connections found. Connect or reconnect an app at ${CONNECTIONS_URL}, then come back and run setup again.`
|
|
6663
|
+
);
|
|
6664
|
+
throw new ZapierCliExitError(
|
|
6665
|
+
"Setup stopped before completion because no active connections are available."
|
|
6666
|
+
);
|
|
6387
6667
|
}
|
|
6388
6668
|
var CONNECTION_PLACEHOLDER = "__ZAPIER_SDK_SLACK_CONNECTION__";
|
|
6389
6669
|
var EMAIL_PLACEHOLDER = "__ZAPIER_SDK_ACCOUNT_EMAIL__";
|
|
6390
6670
|
var APP_PLACEHOLDER = "__ZAPIER_SDK_SLACK_APP__";
|
|
6391
6671
|
var SLACK_SLUG = "slack";
|
|
6672
|
+
var SLACK_RETRY_QUESTION = "Slack email for one retry after the failed test:";
|
|
6392
6673
|
function getSlackTestPath(context) {
|
|
6393
6674
|
const extension = context.projectLanguage === "typescript" ? "ts" : "mjs";
|
|
6394
6675
|
return join("src", "sdk-demo", `zapier-slack-test.${extension}`);
|
|
@@ -6398,7 +6679,7 @@ function getSlackTestTemplatePath(context) {
|
|
|
6398
6679
|
return join(TEMPLATES_DIR, "setup", `zapier-slack-test.${extension}`);
|
|
6399
6680
|
}
|
|
6400
6681
|
async function findSlackConnection(context) {
|
|
6401
|
-
const page = await
|
|
6682
|
+
const page = await context.load({
|
|
6402
6683
|
promise: context.imports.listConnections({
|
|
6403
6684
|
owner: "me",
|
|
6404
6685
|
app: SLACK_SLUG,
|
|
@@ -6419,17 +6700,22 @@ function renderSlackTest({
|
|
|
6419
6700
|
}) {
|
|
6420
6701
|
return readFileSync(getSlackTestTemplatePath(context), "utf8").replace(APP_PLACEHOLDER, () => toTemplateString(app)).replace(CONNECTION_PLACEHOLDER, () => toTemplateString(connection)).replace(EMAIL_PLACEHOLDER, () => toTemplateString(email));
|
|
6421
6702
|
}
|
|
6422
|
-
function
|
|
6703
|
+
async function createAndRunSlackTest({
|
|
6423
6704
|
context,
|
|
6424
|
-
app,
|
|
6425
6705
|
connection,
|
|
6426
|
-
email
|
|
6706
|
+
email,
|
|
6707
|
+
overwrite = true
|
|
6427
6708
|
}) {
|
|
6428
6709
|
const slackTestPath = getSlackTestPath(context);
|
|
6429
6710
|
const path2 = join(context.cwd, slackTestPath);
|
|
6430
6711
|
if (!existsSync(dirname(path2))) mkdirSync(dirname(path2), { recursive: true });
|
|
6431
|
-
writeFileSync(
|
|
6712
|
+
writeFileSync(
|
|
6713
|
+
path2,
|
|
6714
|
+
renderSlackTest({ context, app: SLACK_SLUG, connection, email }),
|
|
6715
|
+
{ flag: overwrite ? "w" : "wx" }
|
|
6716
|
+
);
|
|
6432
6717
|
console.log(`Created ${slackTestPath}.`);
|
|
6718
|
+
return trySlackTest(context);
|
|
6433
6719
|
}
|
|
6434
6720
|
async function executeSlackTest(context) {
|
|
6435
6721
|
const slackTestPath = getSlackTestPath(context);
|
|
@@ -6457,75 +6743,76 @@ async function executeSlackTest(context) {
|
|
|
6457
6743
|
async function trySlackTest(context) {
|
|
6458
6744
|
try {
|
|
6459
6745
|
await executeSlackTest(context);
|
|
6746
|
+
(context.outcomes ?? (context.outcomes = {})).slack = "succeeded";
|
|
6460
6747
|
return true;
|
|
6461
6748
|
} catch (error) {
|
|
6749
|
+
(context.outcomes ?? (context.outcomes = {})).slack = "failed";
|
|
6462
6750
|
const message = error instanceof Error ? error.message : String(error);
|
|
6463
6751
|
console.warn(`Slack demo failed: ${message}. Setup will continue.`);
|
|
6464
6752
|
return false;
|
|
6465
6753
|
}
|
|
6466
6754
|
}
|
|
6467
|
-
async function
|
|
6755
|
+
async function runSlackStep({
|
|
6756
|
+
context,
|
|
6757
|
+
step,
|
|
6758
|
+
connection
|
|
6759
|
+
}) {
|
|
6468
6760
|
if (!context.accountEmail) {
|
|
6469
6761
|
console.log("No account email found; skipping the Slack demo.");
|
|
6470
|
-
return;
|
|
6762
|
+
return "handoff";
|
|
6471
6763
|
}
|
|
6472
|
-
|
|
6473
|
-
if (!slackConnection) {
|
|
6764
|
+
if (!connection) {
|
|
6474
6765
|
console.log("No active Slack connection found; skipping the Slack demo.");
|
|
6475
|
-
return;
|
|
6766
|
+
return "handoff";
|
|
6476
6767
|
}
|
|
6477
|
-
|
|
6478
|
-
|
|
6479
|
-
answer: context.createAnswer(),
|
|
6480
|
-
controller: context.setupController,
|
|
6481
|
-
message: `Run the Slack self-DM test using ${context.accountEmail}?`,
|
|
6482
|
-
defaultValue: true
|
|
6483
|
-
});
|
|
6484
|
-
if (!shouldRun) return;
|
|
6485
|
-
const path2 = join(context.cwd, slackTestPath);
|
|
6486
|
-
if (existsSync(path2)) {
|
|
6487
|
-
const shouldOverwrite = await resolveSetupConfirm({
|
|
6768
|
+
if (step === "slack-retry") {
|
|
6769
|
+
const email = await resolveSetupInput({
|
|
6488
6770
|
answer: context.createAnswer(),
|
|
6489
6771
|
controller: context.setupController,
|
|
6490
|
-
message:
|
|
6491
|
-
defaultValue: false
|
|
6772
|
+
message: SLACK_RETRY_QUESTION
|
|
6492
6773
|
});
|
|
6493
|
-
if (
|
|
6494
|
-
|
|
6495
|
-
|
|
6496
|
-
|
|
6497
|
-
|
|
6498
|
-
message: `Run the existing ${slackTestPath}?`,
|
|
6499
|
-
defaultValue: false
|
|
6774
|
+
if (email)
|
|
6775
|
+
await createAndRunSlackTest({
|
|
6776
|
+
context,
|
|
6777
|
+
connection: connection.id,
|
|
6778
|
+
email
|
|
6500
6779
|
});
|
|
6501
|
-
|
|
6502
|
-
|
|
6503
|
-
}
|
|
6780
|
+
else console.log("Skipping the Slack test.");
|
|
6781
|
+
return "handoff";
|
|
6504
6782
|
}
|
|
6505
|
-
|
|
6506
|
-
|
|
6507
|
-
context,
|
|
6508
|
-
app: SLACK_SLUG,
|
|
6509
|
-
connection: slackConnection.id,
|
|
6510
|
-
email
|
|
6511
|
-
});
|
|
6512
|
-
if (await trySlackTest(context)) return;
|
|
6513
|
-
email = await resolveSetupInput({
|
|
6783
|
+
const slackTestPath = getSlackTestPath(context);
|
|
6784
|
+
const shouldRun = await resolveSetupConfirm({
|
|
6514
6785
|
answer: context.createAnswer(),
|
|
6515
6786
|
controller: context.setupController,
|
|
6516
|
-
message: "Slack
|
|
6787
|
+
message: step === "slack" ? `Run the Slack self-DM test using ${context.accountEmail}?` : step === "slack-overwrite" ? `${slackTestPath} already exists. Is it okay to overwrite it?` : `Run the existing ${slackTestPath}?`,
|
|
6788
|
+
defaultValue: step === "slack"
|
|
6517
6789
|
});
|
|
6518
|
-
if (!
|
|
6519
|
-
|
|
6520
|
-
|
|
6790
|
+
if (!shouldRun) {
|
|
6791
|
+
if (step === "slack-overwrite") {
|
|
6792
|
+
console.log("Keeping the existing Slack test file.");
|
|
6793
|
+
return "slack-existing";
|
|
6794
|
+
}
|
|
6795
|
+
return "handoff";
|
|
6521
6796
|
}
|
|
6522
|
-
|
|
6797
|
+
if (step === "slack" && existsSync(join(context.cwd, slackTestPath)))
|
|
6798
|
+
return "slack-overwrite";
|
|
6799
|
+
if (step === "slack-existing") {
|
|
6800
|
+
await trySlackTest(context);
|
|
6801
|
+
return "handoff";
|
|
6802
|
+
}
|
|
6803
|
+
const succeeded = await createAndRunSlackTest({
|
|
6523
6804
|
context,
|
|
6524
|
-
|
|
6525
|
-
|
|
6526
|
-
|
|
6805
|
+
connection: connection.id,
|
|
6806
|
+
email: context.accountEmail,
|
|
6807
|
+
overwrite: step === "slack-overwrite"
|
|
6527
6808
|
});
|
|
6528
|
-
|
|
6809
|
+
return succeeded ? "handoff" : "slack-retry";
|
|
6810
|
+
}
|
|
6811
|
+
async function offerSlackTest(context) {
|
|
6812
|
+
const connection = context.accountEmail ? await findSlackConnection(context) : void 0;
|
|
6813
|
+
let step = "slack";
|
|
6814
|
+
while (step !== "handoff")
|
|
6815
|
+
step = await runSlackStep({ context, step, connection });
|
|
6529
6816
|
}
|
|
6530
6817
|
|
|
6531
6818
|
// src/plugins/setup/skill.ts
|
|
@@ -6551,7 +6838,9 @@ async function installOptionalSkill(context) {
|
|
|
6551
6838
|
cwd: context.cwd,
|
|
6552
6839
|
capture: true
|
|
6553
6840
|
});
|
|
6841
|
+
(context.outcomes ?? (context.outcomes = {})).skill = "succeeded";
|
|
6554
6842
|
} catch {
|
|
6843
|
+
(context.outcomes ?? (context.outcomes = {})).skill = "failed";
|
|
6555
6844
|
console.warn(
|
|
6556
6845
|
`Could not install the optional Zapier SDK skill. To install it manually, run: ${formatPackageCommand(command)}`
|
|
6557
6846
|
);
|
|
@@ -6608,6 +6897,177 @@ async function runWizard(context) {
|
|
|
6608
6897
|
console.log(chalk5.bgCyan.black.bold(" Next steps "));
|
|
6609
6898
|
await openAgentHandoff(context);
|
|
6610
6899
|
}
|
|
6900
|
+
var UNPROMPTED_STEPS = [
|
|
6901
|
+
"project",
|
|
6902
|
+
"confirm-account",
|
|
6903
|
+
"connections"
|
|
6904
|
+
];
|
|
6905
|
+
var ZapierSetupQuestionSignal = class extends ZapierSignal {
|
|
6906
|
+
constructor(question) {
|
|
6907
|
+
super(question.message);
|
|
6908
|
+
this.question = question;
|
|
6909
|
+
this.name = "ZapierSetupQuestionSignal";
|
|
6910
|
+
this.code = "ZAPIER_SETUP_QUESTION";
|
|
6911
|
+
}
|
|
6912
|
+
};
|
|
6913
|
+
function isZapierSetupQuestionSignal(error) {
|
|
6914
|
+
return isZapierSignal(error) && error.code === "ZAPIER_SETUP_QUESTION";
|
|
6915
|
+
}
|
|
6916
|
+
function createSetupAnswer({
|
|
6917
|
+
answer,
|
|
6918
|
+
execute
|
|
6919
|
+
}) {
|
|
6920
|
+
let answered = false;
|
|
6921
|
+
return async ({ result: { question } }) => {
|
|
6922
|
+
if (question.type === "collection")
|
|
6923
|
+
throw new ZapierCliValidationError(
|
|
6924
|
+
"Setup expects a selection or text input."
|
|
6925
|
+
);
|
|
6926
|
+
if (!execute) throw new ZapierSetupQuestionSignal(question);
|
|
6927
|
+
if (answered)
|
|
6928
|
+
throw new ZapierCliValidationError(
|
|
6929
|
+
"Each setup step can answer only one question. Inspect the next question with --agent and run it in a separate command."
|
|
6930
|
+
);
|
|
6931
|
+
answered = true;
|
|
6932
|
+
const confirmation = question.type === "select" && question.choices.some(({ value }) => value === "true") && question.choices.some(({ value }) => value === "false");
|
|
6933
|
+
if (answer === void 0 && !confirmation)
|
|
6934
|
+
throw new ZapierCliValidationError(
|
|
6935
|
+
"This setup step requires --answer. Inspect it with --agent and pass the user's selection or input."
|
|
6936
|
+
);
|
|
6937
|
+
if (question.type === "input")
|
|
6938
|
+
return { type: "custom", value: answer?.trim() ?? "" };
|
|
6939
|
+
const selected = answer?.trim() ?? question.choices[0].value;
|
|
6940
|
+
if (!question.choices.some(({ value }) => value === selected))
|
|
6941
|
+
throw new ZapierCliValidationError(
|
|
6942
|
+
`Answer must be one of: ${question.choices.map(({ value }) => value).join(", ")}.`
|
|
6943
|
+
);
|
|
6944
|
+
return { type: "choose", value: selected };
|
|
6945
|
+
};
|
|
6946
|
+
}
|
|
6947
|
+
async function runSetupStep({
|
|
6948
|
+
context,
|
|
6949
|
+
target
|
|
6950
|
+
}) {
|
|
6951
|
+
switch (target.step) {
|
|
6952
|
+
case "directory":
|
|
6953
|
+
await chooseDirectory(context);
|
|
6954
|
+
return { step: "project" };
|
|
6955
|
+
case "project":
|
|
6956
|
+
await ensureSupportedNode(context);
|
|
6957
|
+
await initializeProject(context);
|
|
6958
|
+
return { step: "install" };
|
|
6959
|
+
case "install":
|
|
6960
|
+
await offerMissingPackageInstall({
|
|
6961
|
+
context,
|
|
6962
|
+
packages: getPackageStates(context).filter(({ declared }) => !declared)
|
|
6963
|
+
});
|
|
6964
|
+
return { step: "sdk-update" };
|
|
6965
|
+
case "sdk-update":
|
|
6966
|
+
case "cli-update": {
|
|
6967
|
+
const name = target.step === "sdk-update" ? "@zapier/zapier-sdk" : "@zapier/zapier-sdk-cli";
|
|
6968
|
+
const packageState = getPackageStates(context).find(
|
|
6969
|
+
({ dependency }) => dependency.name === name
|
|
6970
|
+
);
|
|
6971
|
+
if (packageState) await offerPackageUpdate({ context, packageState });
|
|
6972
|
+
return { step: target.step === "sdk-update" ? "cli-update" : "skill" };
|
|
6973
|
+
}
|
|
6974
|
+
case "skill":
|
|
6975
|
+
await installOptionalSkill(context);
|
|
6976
|
+
return { step: "account" };
|
|
6977
|
+
case "account":
|
|
6978
|
+
case "account-flow":
|
|
6979
|
+
case "account-environment":
|
|
6980
|
+
case "authorize":
|
|
6981
|
+
case "confirm-account":
|
|
6982
|
+
return runAuthenticationStep({ context, options: target });
|
|
6983
|
+
case "connections":
|
|
6984
|
+
if (!await showActiveConnections(context)) {
|
|
6985
|
+
console.log(
|
|
6986
|
+
`No active connections found. Ask the user to connect or reconnect an app at ${CONNECTIONS_URL}. After they confirm, run this exact command:
|
|
6987
|
+
${formatSetupCommand({ context, target })}
|
|
6988
|
+
Success means it prints the active connections table.`
|
|
6989
|
+
);
|
|
6990
|
+
throw new ZapierCliExitError(
|
|
6991
|
+
"Setup stopped until an app connection is active."
|
|
6992
|
+
);
|
|
6993
|
+
}
|
|
6994
|
+
console.log(
|
|
6995
|
+
"Show the user every app and connection title in the table above before continuing. Do not just report that connections were found."
|
|
6996
|
+
);
|
|
6997
|
+
return { step: "slack" };
|
|
6998
|
+
case "slack":
|
|
6999
|
+
case "slack-overwrite":
|
|
7000
|
+
case "slack-existing":
|
|
7001
|
+
case "slack-retry": {
|
|
7002
|
+
const { data: profile } = await context.imports.getProfile({});
|
|
7003
|
+
context.accountEmail = profile.email;
|
|
7004
|
+
return {
|
|
7005
|
+
step: await runSlackStep({
|
|
7006
|
+
context,
|
|
7007
|
+
step: target.step,
|
|
7008
|
+
connection: await findSlackConnection(context)
|
|
7009
|
+
})
|
|
7010
|
+
};
|
|
7011
|
+
}
|
|
7012
|
+
}
|
|
7013
|
+
}
|
|
7014
|
+
async function runAgentSetup({
|
|
7015
|
+
context,
|
|
7016
|
+
options
|
|
7017
|
+
}) {
|
|
7018
|
+
if (options.agent && options.nonInteractive)
|
|
7019
|
+
throw new ZapierCliValidationError(
|
|
7020
|
+
"Use --agent to inspect a step or --non-interactive to execute it, not both."
|
|
7021
|
+
);
|
|
7022
|
+
if (!options.agent && !options.nonInteractive)
|
|
7023
|
+
throw new ZapierCliValidationError(
|
|
7024
|
+
options.step ? "Use --agent to inspect the named step or --non-interactive to execute it." : "--answer, --flow, and --language require a setup step."
|
|
7025
|
+
);
|
|
7026
|
+
if (options.nonInteractive && !options.step)
|
|
7027
|
+
throw new ZapierCliValidationError(
|
|
7028
|
+
"--non-interactive requires --step. Start with setup --agent and follow its printed commands."
|
|
7029
|
+
);
|
|
7030
|
+
if (options.answer !== void 0 && !options.nonInteractive)
|
|
7031
|
+
throw new ZapierCliValidationError("--answer requires --non-interactive.");
|
|
7032
|
+
context.cwd = realpathSync(context.cwd);
|
|
7033
|
+
context.projectLanguage = options.language ?? detectProjectLanguage({
|
|
7034
|
+
cwd: context.cwd,
|
|
7035
|
+
hasExistingProject: existsSync(join(context.cwd, "package.json"))
|
|
7036
|
+
});
|
|
7037
|
+
let target = {
|
|
7038
|
+
step: options.step ?? "directory",
|
|
7039
|
+
flow: options.flow
|
|
7040
|
+
};
|
|
7041
|
+
let execute = options.nonInteractive === true;
|
|
7042
|
+
while (true) {
|
|
7043
|
+
const { step, flow } = target;
|
|
7044
|
+
if (step === "handoff") return printAgentCompletion({ context });
|
|
7045
|
+
if (!execute && UNPROMPTED_STEPS.includes(step))
|
|
7046
|
+
return printAgentStep({ context, target });
|
|
7047
|
+
const answer = createSetupAnswer({
|
|
7048
|
+
answer: execute ? options.answer : void 0,
|
|
7049
|
+
execute
|
|
7050
|
+
});
|
|
7051
|
+
context.createAnswer = () => answer;
|
|
7052
|
+
try {
|
|
7053
|
+
const next = await runSetupStep({ context, target: { step, flow } });
|
|
7054
|
+
if (options.language) context.projectLanguage = options.language;
|
|
7055
|
+
if (execute)
|
|
7056
|
+
for (const [operation, outcome] of Object.entries(
|
|
7057
|
+
context.outcomes ?? {}
|
|
7058
|
+
))
|
|
7059
|
+
console.log(`${operation}: ${outcome}`);
|
|
7060
|
+
target = next;
|
|
7061
|
+
execute = false;
|
|
7062
|
+
} catch (error) {
|
|
7063
|
+
if (isZapierSetupQuestionSignal(error))
|
|
7064
|
+
return printAgentStep({ context, target, question: error.question });
|
|
7065
|
+
if (error instanceof ZapierCliUserCancellationError)
|
|
7066
|
+
return printAgentCompletion({ context, cancelled: true });
|
|
7067
|
+
throw error;
|
|
7068
|
+
}
|
|
7069
|
+
}
|
|
7070
|
+
}
|
|
6611
7071
|
|
|
6612
7072
|
// src/plugins/setup/index.ts
|
|
6613
7073
|
var getProfileRef = declareMethod({ id: "getProfile" });
|
|
@@ -6632,10 +7092,19 @@ var setupPlugin = defineMethod({
|
|
|
6632
7092
|
supportsJsonOutput: false,
|
|
6633
7093
|
inputSchema: SetupSchema,
|
|
6634
7094
|
output: "raw",
|
|
6635
|
-
run: async ({ imports }) => {
|
|
7095
|
+
run: async ({ imports, input }) => {
|
|
7096
|
+
if (input.agent || input.nonInteractive || [input.step, input.answer, input.flow, input.language].some(
|
|
7097
|
+
(value) => value !== void 0
|
|
7098
|
+
)) {
|
|
7099
|
+
await runAgentSetup({
|
|
7100
|
+
context: createWizardContext({ imports, agent: true }),
|
|
7101
|
+
options: input
|
|
7102
|
+
});
|
|
7103
|
+
return;
|
|
7104
|
+
}
|
|
6636
7105
|
if (process.stdin.isTTY !== true || process.stdout.isTTY !== true) {
|
|
6637
|
-
throw new
|
|
6638
|
-
"`zapier-sdk setup` requires an interactive terminal."
|
|
7106
|
+
throw new ZapierCliValidationError(
|
|
7107
|
+
"`zapier-sdk setup` requires an interactive terminal. AI agents and other callers without a terminal should run `zapier-sdk setup --agent` and follow its printed steps."
|
|
6639
7108
|
);
|
|
6640
7109
|
}
|
|
6641
7110
|
await runWizard(createWizardContext({ imports }));
|
|
@@ -7130,39 +7599,6 @@ var watchTriggerInboxCliPlugin = defineMethod({
|
|
|
7130
7599
|
return doWatch();
|
|
7131
7600
|
}
|
|
7132
7601
|
});
|
|
7133
|
-
var createWorkflowRef = declareMethod$1({ id: "createWorkflow" });
|
|
7134
|
-
function createPublicInputSchema(schema) {
|
|
7135
|
-
return z.object(
|
|
7136
|
-
Object.fromEntries(
|
|
7137
|
-
Object.entries(schema.shape).filter(
|
|
7138
|
-
([name]) => !INTERNAL_CREATE_WORKFLOW_FIELDS.includes(
|
|
7139
|
-
name
|
|
7140
|
-
)
|
|
7141
|
-
)
|
|
7142
|
-
)
|
|
7143
|
-
).describe(schema.description ?? "Create a workflow");
|
|
7144
|
-
}
|
|
7145
|
-
var CreateWorkflowCliOptionsSchema = createPublicInputSchema(
|
|
7146
|
-
CreateWorkflowOptionsSchema
|
|
7147
|
-
);
|
|
7148
|
-
var createWorkflowCliPlugin = defineMethod$1({
|
|
7149
|
-
namespace: "cli",
|
|
7150
|
-
name: "createWorkflow",
|
|
7151
|
-
imports: [createWorkflowRef],
|
|
7152
|
-
type: "create",
|
|
7153
|
-
itemType: "Workflow",
|
|
7154
|
-
categories: ["code-workflow"],
|
|
7155
|
-
stability: "experimental",
|
|
7156
|
-
inputSchema: CreateWorkflowCliOptionsSchema,
|
|
7157
|
-
outputSchema: CreateWorkflowResponseSchema,
|
|
7158
|
-
skipOutputValidation: true,
|
|
7159
|
-
output: "raw",
|
|
7160
|
-
run: ({ imports, input }) => imports.createWorkflow({
|
|
7161
|
-
...input,
|
|
7162
|
-
creationHarness: getAgent(),
|
|
7163
|
-
creationAccessMethod: "sdk_cli"
|
|
7164
|
-
})
|
|
7165
|
-
});
|
|
7166
7602
|
function warningBlock({
|
|
7167
7603
|
banner,
|
|
7168
7604
|
message,
|
|
@@ -7188,9 +7624,9 @@ function collectDeprecationNoticeAndForward(event, onEvent) {
|
|
|
7188
7624
|
}
|
|
7189
7625
|
|
|
7190
7626
|
// package.json with { type: 'json' }
|
|
7191
|
-
var
|
|
7627
|
+
var package_default2 = {
|
|
7192
7628
|
name: "@zapier/zapier-sdk-cli",
|
|
7193
|
-
version: "0.
|
|
7629
|
+
version: "0.87.1"};
|
|
7194
7630
|
|
|
7195
7631
|
// src/sdk.ts
|
|
7196
7632
|
var warnedDeprecatedMethods = /* @__PURE__ */ new Set();
|
|
@@ -7259,7 +7695,7 @@ function createZapierCliSdk(options = {}) {
|
|
|
7259
7695
|
const stackOptions = {
|
|
7260
7696
|
...sdkOptions,
|
|
7261
7697
|
eventEmission: { ...sdkOptions.eventEmission, callContext: "cli" },
|
|
7262
|
-
callerPackage: { name:
|
|
7698
|
+
callerPackage: { name: package_default2.name, version: package_default2.version },
|
|
7263
7699
|
// Collected for the boxed stderr warning rendered in the exit path. The
|
|
7264
7700
|
// box is additive: the SDK's own inline warn still fires, so the notice
|
|
7265
7701
|
// cannot be hidden by any rendering path.
|
|
@@ -7271,12 +7707,10 @@ function createZapierCliSdk(options = {}) {
|
|
|
7271
7707
|
exports: [
|
|
7272
7708
|
omitExports$1(zapierExperimentalSdkPlugin, [
|
|
7273
7709
|
"drainTriggerInbox",
|
|
7274
|
-
"watchTriggerInbox"
|
|
7275
|
-
"createWorkflow"
|
|
7710
|
+
"watchTriggerInbox"
|
|
7276
7711
|
]),
|
|
7277
7712
|
drainTriggerInboxCliPlugin,
|
|
7278
7713
|
watchTriggerInboxCliPlugin,
|
|
7279
|
-
createWorkflowCliPlugin,
|
|
7280
7714
|
loginPlugin,
|
|
7281
7715
|
signupPlugin,
|
|
7282
7716
|
logoutPlugin,
|