@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.
@@ -31,6 +31,7 @@ var packageJsonLib = require('package-json');
31
31
  var semver = require('semver');
32
32
  var crossSpawn = require('cross-spawn');
33
33
  var wrapAnsi = require('wrap-ansi');
34
+ var module$1 = require('module');
34
35
  var Table = require('cli-table3');
35
36
 
36
37
  var _documentCurrentScript = typeof document !== 'undefined' ? document.currentScript : null;
@@ -1035,6 +1036,7 @@ var SENSITIVE_FLAGS = [
1035
1036
  "--credentials-client-id",
1036
1037
  "--credentials-base-url",
1037
1038
  "--callback-url",
1039
+ "--answer",
1038
1040
  "--user",
1039
1041
  "--header",
1040
1042
  "-H",
@@ -1271,6 +1273,7 @@ var searchSelect = core.createPrompt(
1271
1273
  );
1272
1274
 
1273
1275
  // src/utils/auth/credentials-picker.ts
1276
+ var CREDENTIALS_PICKER_QUESTION = "Select credentials or add a new account:";
1274
1277
  function isSameCredentials(left, right) {
1275
1278
  return left.name === right.name && left.baseUrl === right.baseUrl;
1276
1279
  }
@@ -1376,7 +1379,7 @@ async function promptForCredentials({
1376
1379
  activeCredentials
1377
1380
  }) {
1378
1381
  return searchSelect({
1379
- message: "Select credentials or add a new account:",
1382
+ message: CREDENTIALS_PICKER_QUESTION,
1380
1383
  source: (term) => buildCredentialsChoices({
1381
1384
  credentials,
1382
1385
  activeCredentials,
@@ -1439,14 +1442,15 @@ async function switchCredentials({
1439
1442
  async function chooseStoredLoginCredentialsAtBaseUrl({
1440
1443
  baseUrl,
1441
1444
  options,
1442
- resolvedCredentials
1445
+ resolvedCredentials,
1446
+ prompt = promptForCredentials
1443
1447
  }) {
1444
1448
  if (resolvedCredentials !== void 0 || authFlowOnlyOptions(options).length > 0) {
1445
1449
  return "none";
1446
1450
  }
1447
1451
  const storedCredentials = listStoredCredentials({ baseUrl });
1448
1452
  if (storedCredentials.length === 0) return "none";
1449
- const selection = await promptForCredentials({
1453
+ const selection = await prompt({
1450
1454
  credentials: storedCredentials,
1451
1455
  activeCredentials: getActiveCredentials({ baseUrl })
1452
1456
  });
@@ -1460,7 +1464,8 @@ async function chooseStoredLoginCredentialsAtBaseUrl({
1460
1464
  }
1461
1465
  async function chooseStoredLoginCredentials({
1462
1466
  imports,
1463
- options
1467
+ options,
1468
+ prompt
1464
1469
  }) {
1465
1470
  const resolvedCredentials = await imports.resolveCredentials();
1466
1471
  const credentialsBaseUrl = await resolveCredentialsBaseUrl({
@@ -1470,7 +1475,8 @@ async function chooseStoredLoginCredentials({
1470
1475
  return chooseStoredLoginCredentialsAtBaseUrl({
1471
1476
  baseUrl: credentialsBaseUrl,
1472
1477
  options,
1473
- resolvedCredentials
1478
+ resolvedCredentials,
1479
+ prompt
1474
1480
  });
1475
1481
  }
1476
1482
  async function confirmNewCredentialsName(name) {
@@ -1841,6 +1847,7 @@ var PendingOauthTransactionSchema = zod.z.object({
1841
1847
  credentialName: zod.z.string().optional(),
1842
1848
  useApprovals: zod.z.boolean(),
1843
1849
  headless: zod.z.boolean().default(false),
1850
+ preserveExistingCredentials: zod.z.boolean().default(false),
1844
1851
  transaction: OauthTransactionSchema
1845
1852
  });
1846
1853
  function savePendingOauthTransaction({
@@ -1850,7 +1857,8 @@ function savePendingOauthTransaction({
1850
1857
  credentialsBaseUrl,
1851
1858
  credentialName,
1852
1859
  useApprovals,
1853
- headless
1860
+ headless,
1861
+ preserveExistingCredentials = false
1854
1862
  }) {
1855
1863
  const pendingTransaction = {
1856
1864
  version: 1,
@@ -1858,6 +1866,7 @@ function savePendingOauthTransaction({
1858
1866
  expiresAt,
1859
1867
  useApprovals,
1860
1868
  headless,
1869
+ preserveExistingCredentials,
1861
1870
  transaction,
1862
1871
  ...credentialsBaseUrl !== void 0 && { credentialsBaseUrl },
1863
1872
  ...credentialName !== void 0 && { credentialName }
@@ -1876,6 +1885,7 @@ function clearPendingOauthTransaction() {
1876
1885
  }
1877
1886
 
1878
1887
  // src/utils/auth/oauth-flow.ts
1888
+ var OAUTH_CALLBACK_QUESTION = "Paste the final OAuth callback URL:";
1879
1889
  var PENDING_OAUTH_RECOVERY_MESSAGE = "Start a new non-interactive login or signup flow to generate a fresh URL.";
1880
1890
  var LOGIN_OAUTH_COPY = {
1881
1891
  flowName: "Login",
@@ -1939,7 +1949,9 @@ async function startPendingOauthFlow({
1939
1949
  entryPoint,
1940
1950
  credentialName,
1941
1951
  useApprovals,
1942
- headless
1952
+ headless,
1953
+ preserveExistingCredentials = false,
1954
+ silent = false
1943
1955
  }) {
1944
1956
  const copy = entryPoint === "signup" ? SIGNUP_OAUTH_COPY : LOGIN_OAUTH_COPY;
1945
1957
  const port = LOGIN_PORTS[0];
@@ -1956,8 +1968,10 @@ async function startPendingOauthFlow({
1956
1968
  credentialsBaseUrl: baseUrl,
1957
1969
  credentialName,
1958
1970
  useApprovals,
1959
- headless
1971
+ headless,
1972
+ preserveExistingCredentials
1960
1973
  });
1974
+ if (silent) return pending;
1961
1975
  process.stderr.write(
1962
1976
  `Open this ${copy.urlLabel} in a browser to continue:
1963
1977
  `
@@ -1983,23 +1997,24 @@ async function startPendingOauthFlow({
1983
1997
  return pending;
1984
1998
  }
1985
1999
  function getPendingOauthFlow({
1986
- entryPoint
2000
+ entryPoint,
2001
+ recoveryMessage = PENDING_OAUTH_RECOVERY_MESSAGE
1987
2002
  }) {
1988
2003
  const pending = getPendingOauthTransaction();
1989
2004
  if (!pending) {
1990
2005
  throw new ZapierCliValidationError(
1991
- `No pending ${entryPoint} OAuth flow found. ${PENDING_OAUTH_RECOVERY_MESSAGE}`
2006
+ `No pending ${entryPoint} OAuth flow found. ${recoveryMessage}`
1992
2007
  );
1993
2008
  }
1994
2009
  if (Date.now() > pending.expiresAt) {
1995
2010
  clearPendingOauthTransaction();
1996
2011
  throw new ZapierCliValidationError(
1997
- `Pending ${pending.entryPoint} OAuth flow has expired. ${PENDING_OAUTH_RECOVERY_MESSAGE}`
2012
+ `Pending ${pending.entryPoint} OAuth flow has expired. ${recoveryMessage}`
1998
2013
  );
1999
2014
  }
2000
2015
  if (pending.entryPoint !== entryPoint) {
2001
2016
  throw new ZapierCliValidationError(
2002
- `Pending OAuth flow is for ${pending.entryPoint}, not ${entryPoint}. ${PENDING_OAUTH_RECOVERY_MESSAGE}`
2017
+ `Pending OAuth flow is for ${pending.entryPoint}, not ${entryPoint}. ${recoveryMessage}`
2003
2018
  );
2004
2019
  }
2005
2020
  return pending;
@@ -2162,7 +2177,7 @@ async function readHeadlessCallbackUrl({
2162
2177
  process.once("SIGINT", handleCancellation);
2163
2178
  process.once("SIGTERM", handleCancellation);
2164
2179
  if (interactive) {
2165
- void rl.question("Paste the final OAuth callback URL: ", {
2180
+ void rl.question(`${OAUTH_CALLBACK_QUESTION} `, {
2166
2181
  signal: abortController.signal
2167
2182
  }).then(settleResolve).catch((error) => {
2168
2183
  if (error instanceof Error && error.name === "AbortError") {
@@ -2649,7 +2664,7 @@ async function runOauthForEntryPoint({
2649
2664
  if (event.type === "callback_accepted") {
2650
2665
  emitSignupSuccess({
2651
2666
  imports,
2652
- isNonInteractive: false,
2667
+ isNonInteractive: !interactive,
2653
2668
  isHeadless: headless === true
2654
2669
  });
2655
2670
  }
@@ -2731,7 +2746,9 @@ async function runAccountAuth({
2731
2746
  options,
2732
2747
  entryPoint,
2733
2748
  storedCredentialsMode = "choose",
2734
- preserveExistingCredentials = false
2749
+ preserveExistingCredentials = false,
2750
+ useBrowserCallback = false,
2751
+ silent = false
2735
2752
  }) {
2736
2753
  if (options.callbackUrl !== void 0) {
2737
2754
  const { callbackUrl } = options;
@@ -2764,7 +2781,8 @@ async function runAccountAuth({
2764
2781
  imports,
2765
2782
  baseUrl: pending.credentialsBaseUrl,
2766
2783
  interactive: false,
2767
- entryPoint
2784
+ entryPoint,
2785
+ preserveExistingCredentials: pending.preserveExistingCredentials
2768
2786
  })) {
2769
2787
  return;
2770
2788
  }
@@ -2841,7 +2859,7 @@ async function runAccountAuth({
2841
2859
  );
2842
2860
  }
2843
2861
  }
2844
- if (entryPoint === "login" && !interactive && providedName === void 0) {
2862
+ if (entryPoint === "login" && !interactive && providedName === void 0 && !preserveExistingCredentials) {
2845
2863
  const activeCredentials = getActiveCredentials({
2846
2864
  baseUrl: credentialsBaseUrl
2847
2865
  });
@@ -2858,7 +2876,7 @@ async function runAccountAuth({
2858
2876
  }
2859
2877
  const timeoutSeconds = parseTimeoutSeconds(options.timeout);
2860
2878
  const useApprovals = options.useApprovals === true;
2861
- if (!interactive) {
2879
+ if (!interactive && (headless || !useBrowserCallback)) {
2862
2880
  await startPendingOauthFlow({
2863
2881
  entryPoint,
2864
2882
  timeoutMs: timeoutSeconds * 1e3,
@@ -2866,7 +2884,9 @@ async function runAccountAuth({
2866
2884
  baseUrl: credentialsBaseUrl,
2867
2885
  credentialName: providedName,
2868
2886
  useApprovals,
2869
- headless
2887
+ headless,
2888
+ preserveExistingCredentials,
2889
+ silent
2870
2890
  });
2871
2891
  return;
2872
2892
  }
@@ -2887,7 +2907,7 @@ async function runAccountAuth({
2887
2907
  credentialName: providedName,
2888
2908
  interactive,
2889
2909
  useApprovals,
2890
- isNonInteractive: false,
2910
+ isNonInteractive: !interactive,
2891
2911
  isHeadless: headless
2892
2912
  });
2893
2913
  }
@@ -4778,6 +4798,7 @@ function createProjectDir({
4778
4798
  var TEMPLATES_DIR = url.fileURLToPath(
4779
4799
  new URL("../templates", (typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('experimental.cjs', document.baseURI).href)))
4780
4800
  );
4801
+ var SOURCE_CLI_PATH = (typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('experimental.cjs', document.baseURI).href)).endsWith(".ts") ? url.fileURLToPath(new URL("./cli.ts", (typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('experimental.cjs', document.baseURI).href)))) : void 0;
4781
4802
 
4782
4803
  // src/plugins/init/steps.ts
4783
4804
  var DEFAULT_TEMPLATE = "basic";
@@ -5488,84 +5509,6 @@ async function checkForUpdates({
5488
5509
  };
5489
5510
  }
5490
5511
  }
5491
- var BRAILLE_BLANK = "\u2800";
5492
- var LIGHTNING_INTERVAL_MILLISECONDS = 140;
5493
- var boltRows = [
5494
- "\u2800\u2800\u2880\u28FE\u2800\u2800\u2800\u2800",
5495
- "\u2800\u2800\u28E0\u28FF\u28FF\u2800\u2800\u2800",
5496
- "\u2800\u28FC\u28FF\u28FF\u28FF\u28C0\u28C0\u2840",
5497
- "\u2808\u2809\u2809\u28FF\u28FF\u28FF\u285F\u2800",
5498
- "\u2800\u2800\u2800\u28FF\u28FF\u280B\u2800\u2800",
5499
- "\u2800\u2800\u2800\u287F\u2801\u2800\u2800\u2800"
5500
- ];
5501
- var boltFillOrder = boltRows.flatMap(
5502
- (row, rowIndex) => [...row].flatMap(
5503
- (cell, columnIndex) => cell === BRAILLE_BLANK ? [] : rowIndex * row.length + columnIndex
5504
- )
5505
- );
5506
- var fillStages = [
5507
- ...Array.from({ length: boltFillOrder.length + 1 }, (_, index) => index),
5508
- boltFillOrder.length,
5509
- 0
5510
- ];
5511
- var boltFillRanks = new Map(
5512
- boltFillOrder.map((cellIndex, fillIndex) => [cellIndex, fillIndex])
5513
- );
5514
- var STATUS_ROW_INDEX = Math.floor(boltRows.length / 2);
5515
- function formatPhase({ label, detail }) {
5516
- return detail ? `${chalk5__default.default.bold(label)} ${chalk5__default.default.dim(detail)}` : chalk5__default.default.bold(label);
5517
- }
5518
- function formatLoaderFrame({
5519
- phase,
5520
- fillStage
5521
- }) {
5522
- return boltRows.map((row, rowIndex) => {
5523
- const bolt = [...row].map((cell, columnIndex) => {
5524
- if (cell === BRAILLE_BLANK) return cell;
5525
- const cellIndex = rowIndex * row.length + columnIndex;
5526
- const fillRank = boltFillRanks.get(cellIndex);
5527
- const isFilled = fillRank !== void 0 && fillRank < fillStage;
5528
- return isFilled ? chalk5__default.default.bold.yellow(cell) : chalk5__default.default.dim.yellow(cell);
5529
- }).join("");
5530
- const status = rowIndex === STATUS_ROW_INDEX ? ` ${formatPhase(phase)}` : "";
5531
- return `${bolt}${status}`;
5532
- }).join("\n");
5533
- }
5534
- async function runWithSetupLoader({
5535
- promise,
5536
- ...phase
5537
- }) {
5538
- const startedAt = Date.now();
5539
- let fillFrameIndex = 0;
5540
- const loader = ora__default.default({
5541
- isEnabled: process.stderr.isTTY === true,
5542
- spinner: { interval: 80, frames: [""] },
5543
- text: formatLoaderFrame({
5544
- phase,
5545
- fillStage: fillStages[fillFrameIndex]
5546
- })
5547
- }).start();
5548
- const animationTimer = setInterval(() => {
5549
- fillFrameIndex = (fillFrameIndex + 1) % fillStages.length;
5550
- loader.text = formatLoaderFrame({
5551
- phase,
5552
- fillStage: fillStages[fillFrameIndex]
5553
- });
5554
- }, LIGHTNING_INTERVAL_MILLISECONDS);
5555
- animationTimer.unref?.();
5556
- try {
5557
- const result = await promise;
5558
- clearInterval(animationTimer);
5559
- const elapsedSeconds = ((Date.now() - startedAt) / 1e3).toFixed(1);
5560
- loader.succeed(`${formatPhase(phase)} ${chalk5__default.default.dim(`${elapsedSeconds}s`)}`);
5561
- return result;
5562
- } catch (error) {
5563
- clearInterval(animationTimer);
5564
- const elapsedSeconds = ((Date.now() - startedAt) / 1e3).toFixed(1);
5565
- loader.fail(`${formatPhase(phase)} ${chalk5__default.default.dim(`${elapsedSeconds}s`)}`);
5566
- throw error;
5567
- }
5568
- }
5569
5512
  function isRecord(value) {
5570
5513
  return typeof value === "object" && value !== null && !Array.isArray(value);
5571
5514
  }
@@ -5851,7 +5794,7 @@ async function offerPackageUpdate({
5851
5794
  }) {
5852
5795
  const { currentVersion, dependency } = packageState;
5853
5796
  if (!currentVersion || !dependency.checkForUpdates) return;
5854
- const update = await runWithSetupLoader({
5797
+ const update = await context.load({
5855
5798
  promise: context.checkForUpdates({
5856
5799
  packageName: dependency.name,
5857
5800
  currentVersion
@@ -5876,28 +5819,108 @@ async function offerPackageUpdate({
5876
5819
  })
5877
5820
  );
5878
5821
  }
5879
- async function prepareDependencies(context) {
5822
+ function getPackageStates(context) {
5880
5823
  const declaredPackageNames = getDeclaredPackageNames(context.cwd);
5881
5824
  const requiredPackages = REQUIRED_PACKAGES.filter(
5882
5825
  ({ typescriptOnly }) => !typescriptOnly || context.projectLanguage === "typescript"
5883
5826
  );
5884
- const packages = requiredPackages.map((dependency) => ({
5827
+ return requiredPackages.map((dependency) => ({
5885
5828
  dependency,
5829
+ declared: declaredPackageNames.has(dependency.name),
5886
5830
  currentVersion: declaredPackageNames.has(dependency.name) ? getInstalledPackageVersion({
5887
5831
  cwd: context.cwd,
5888
5832
  packageName: dependency.name
5889
5833
  }) : void 0
5890
5834
  }));
5835
+ }
5836
+ async function prepareDependencies(context) {
5837
+ const packages = getPackageStates(context);
5891
5838
  await offerMissingPackageInstall({
5892
5839
  context,
5893
- packages: packages.filter(
5894
- ({ dependency }) => !declaredPackageNames.has(dependency.name)
5895
- )
5840
+ packages: packages.filter(({ declared }) => !declared)
5896
5841
  });
5897
5842
  for (const packageState of packages) {
5898
5843
  await offerPackageUpdate({ context, packageState });
5899
5844
  }
5900
5845
  }
5846
+ var BRAILLE_BLANK = "\u2800";
5847
+ var LIGHTNING_INTERVAL_MILLISECONDS = 140;
5848
+ var boltRows = [
5849
+ "\u2800\u2800\u2880\u28FE\u2800\u2800\u2800\u2800",
5850
+ "\u2800\u2800\u28E0\u28FF\u28FF\u2800\u2800\u2800",
5851
+ "\u2800\u28FC\u28FF\u28FF\u28FF\u28C0\u28C0\u2840",
5852
+ "\u2808\u2809\u2809\u28FF\u28FF\u28FF\u285F\u2800",
5853
+ "\u2800\u2800\u2800\u28FF\u28FF\u280B\u2800\u2800",
5854
+ "\u2800\u2800\u2800\u287F\u2801\u2800\u2800\u2800"
5855
+ ];
5856
+ var boltFillOrder = boltRows.flatMap(
5857
+ (row, rowIndex) => [...row].flatMap(
5858
+ (cell, columnIndex) => cell === BRAILLE_BLANK ? [] : rowIndex * row.length + columnIndex
5859
+ )
5860
+ );
5861
+ var fillStages = [
5862
+ ...Array.from({ length: boltFillOrder.length + 1 }, (_, index) => index),
5863
+ boltFillOrder.length,
5864
+ 0
5865
+ ];
5866
+ var boltFillRanks = new Map(
5867
+ boltFillOrder.map((cellIndex, fillIndex) => [cellIndex, fillIndex])
5868
+ );
5869
+ var STATUS_ROW_INDEX = Math.floor(boltRows.length / 2);
5870
+ function formatPhase({ label, detail }) {
5871
+ return detail ? `${chalk5__default.default.bold(label)} ${chalk5__default.default.dim(detail)}` : chalk5__default.default.bold(label);
5872
+ }
5873
+ function formatLoaderFrame({
5874
+ phase,
5875
+ fillStage
5876
+ }) {
5877
+ return boltRows.map((row, rowIndex) => {
5878
+ const bolt = [...row].map((cell, columnIndex) => {
5879
+ if (cell === BRAILLE_BLANK) return cell;
5880
+ const cellIndex = rowIndex * row.length + columnIndex;
5881
+ const fillRank = boltFillRanks.get(cellIndex);
5882
+ const isFilled = fillRank !== void 0 && fillRank < fillStage;
5883
+ return isFilled ? chalk5__default.default.bold.yellow(cell) : chalk5__default.default.dim.yellow(cell);
5884
+ }).join("");
5885
+ const status = rowIndex === STATUS_ROW_INDEX ? ` ${formatPhase(phase)}` : "";
5886
+ return `${bolt}${status}`;
5887
+ }).join("\n");
5888
+ }
5889
+ async function runWithSetupLoader({
5890
+ promise,
5891
+ ...phase
5892
+ }) {
5893
+ const startedAt = Date.now();
5894
+ let fillFrameIndex = 0;
5895
+ const loader = ora__default.default({
5896
+ isEnabled: process.stderr.isTTY === true,
5897
+ spinner: { interval: 80, frames: [""] },
5898
+ text: formatLoaderFrame({
5899
+ phase,
5900
+ fillStage: fillStages[fillFrameIndex]
5901
+ })
5902
+ }).start();
5903
+ const animationTimer = setInterval(() => {
5904
+ fillFrameIndex = (fillFrameIndex + 1) % fillStages.length;
5905
+ loader.text = formatLoaderFrame({
5906
+ phase,
5907
+ fillStage: fillStages[fillFrameIndex]
5908
+ });
5909
+ }, LIGHTNING_INTERVAL_MILLISECONDS);
5910
+ animationTimer.unref?.();
5911
+ try {
5912
+ const result = await promise;
5913
+ clearInterval(animationTimer);
5914
+ const elapsedSeconds = ((Date.now() - startedAt) / 1e3).toFixed(1);
5915
+ loader.succeed(`${formatPhase(phase)} ${chalk5__default.default.dim(`${elapsedSeconds}s`)}`);
5916
+ return result;
5917
+ } catch (error) {
5918
+ clearInterval(animationTimer);
5919
+ const elapsedSeconds = ((Date.now() - startedAt) / 1e3).toFixed(1);
5920
+ loader.fail(`${formatPhase(phase)} ${chalk5__default.default.dim(`${elapsedSeconds}s`)}`);
5921
+ throw error;
5922
+ }
5923
+ }
5901
5924
 
5902
5925
  // src/plugins/setup/package-commands.ts
5903
5926
  function createLocalPackageCommand({
@@ -5936,7 +5959,10 @@ function formatPackageCommand({
5936
5959
  command,
5937
5960
  args
5938
5961
  }) {
5939
- return [command, ...args].join(" ");
5962
+ return [command, ...args].map(quoteShellArgument).join(" ");
5963
+ }
5964
+ function quoteShellArgument(value) {
5965
+ return /^[a-zA-Z0-9_@%+=:,./-]+$/.test(value) ? value : `'${value.replace(/'/g, `'"'"'`)}'`;
5940
5966
  }
5941
5967
 
5942
5968
  // src/plugins/setup/project.ts
@@ -6041,13 +6067,14 @@ async function runCommand({
6041
6067
  args = [],
6042
6068
  cwd,
6043
6069
  capture = true,
6044
- input
6070
+ input,
6071
+ load = runWithSetupLoader
6045
6072
  }) {
6046
6073
  const commandLabel = `$ ${command} ${args.join(" ")}`.trim();
6047
6074
  const execute = capture ? executeCapturedCommand({ command, args, cwd, input }) : executeStreamingCommand({ command, args, cwd, input });
6048
6075
  try {
6049
6076
  if (capture) {
6050
- return await runWithSetupLoader({
6077
+ return await load({
6051
6078
  promise: execute,
6052
6079
  label: "Running command",
6053
6080
  detail: commandLabel
@@ -6131,7 +6158,7 @@ function detectProjectLanguage({
6131
6158
  );
6132
6159
  return hasTypeScript ? "typescript" : "javascript";
6133
6160
  }
6134
- async function prepareProject(context) {
6161
+ async function initializeProject(context) {
6135
6162
  context.packageManager = detectPackageManager(context.cwd).name;
6136
6163
  console.log(`Using ${context.packageManager} for dependency installs.`);
6137
6164
  const hasExistingProject = fs.existsSync(path.join(context.cwd, "package.json"));
@@ -6147,182 +6174,161 @@ async function prepareProject(context) {
6147
6174
  cwd: context.cwd
6148
6175
  });
6149
6176
  }
6177
+ }
6178
+ async function prepareProject(context) {
6179
+ await initializeProject(context);
6150
6180
  await prepareDependencies(context);
6151
6181
  }
6152
6182
 
6153
6183
  // src/plugins/setup/context.ts
6154
6184
  function createWizardContext({
6155
- imports
6185
+ imports,
6186
+ agent = false
6156
6187
  }) {
6157
- return {
6188
+ const context = {
6158
6189
  cwd: process.cwd(),
6190
+ load: agent ? ({ promise }) => promise : runWithSetupLoader,
6159
6191
  imports,
6160
6192
  createAnswer: () => createCliAnswer({ mode: "closed-list" }),
6161
6193
  setupController: createSetupController(),
6162
- selectStoredLoginCredentials: () => chooseStoredLoginCredentials({ imports, options: {} }),
6163
- authenticateNewAccount: ({ entryPoint, headless }) => runAccountAuth({
6194
+ selectStoredLoginCredentials: () => chooseStoredLoginCredentials({
6195
+ imports,
6196
+ options: {},
6197
+ ...agent ? {
6198
+ prompt: async (options) => {
6199
+ const credentialChoices = buildCredentialsChoices(options);
6200
+ const choices = credentialChoices.map(({ name, value }) => ({
6201
+ label: name,
6202
+ value: value.action === "create" ? "create" : `account:${value.credentials.name}`,
6203
+ result: value
6204
+ }));
6205
+ const selected = await resolveSetupSelect({
6206
+ answer: context.createAnswer(),
6207
+ controller: context.setupController,
6208
+ message: CREDENTIALS_PICKER_QUESTION,
6209
+ choices
6210
+ });
6211
+ const selectedIndex = choices.findIndex(
6212
+ ({ value }) => value === selected
6213
+ );
6214
+ return choices[selectedIndex].result;
6215
+ }
6216
+ } : {}
6217
+ }),
6218
+ authenticateNewAccount: ({ entryPoint, ...options }) => runAccountAuth({
6164
6219
  imports,
6165
- options: { headless },
6220
+ options,
6166
6221
  entryPoint,
6167
6222
  storedCredentialsMode: "fresh",
6168
- preserveExistingCredentials: true
6223
+ preserveExistingCredentials: true,
6224
+ useBrowserCallback: agent && options.headless === false,
6225
+ silent: agent && options.headless
6169
6226
  }),
6170
6227
  checkForUpdates,
6171
6228
  openUrl: async (url) => {
6172
6229
  await open__default.default(url);
6173
6230
  },
6174
- runCommand,
6231
+ runCommand: agent ? (options) => runCommand({
6232
+ ...options,
6233
+ capture: true,
6234
+ load: ({ promise, detail }) => {
6235
+ console.log(detail);
6236
+ return promise;
6237
+ }
6238
+ }) : runCommand,
6175
6239
  packageManager: detectPackageManager(process.cwd()).name,
6176
6240
  projectLanguage: "typescript"
6177
6241
  };
6242
+ return context;
6243
+ }
6244
+ var SetupStepSchema = zod.z.enum([
6245
+ "directory",
6246
+ "project",
6247
+ "install",
6248
+ "sdk-update",
6249
+ "cli-update",
6250
+ "skill",
6251
+ "account",
6252
+ "account-flow",
6253
+ "account-environment",
6254
+ "authorize",
6255
+ "confirm-account",
6256
+ "connections",
6257
+ "slack",
6258
+ "slack-overwrite",
6259
+ "slack-existing",
6260
+ "slack-retry",
6261
+ "handoff"
6262
+ ]);
6263
+ var SetupSchema = zod.z.object({
6264
+ agent: zod.z.boolean().optional().describe(
6265
+ "For AI agents and callers without a terminal: print the next setup question and the exact command to run for each answer."
6266
+ ),
6267
+ step: SetupStepSchema.optional().describe(
6268
+ "Setup operation to inspect or execute."
6269
+ ),
6270
+ nonInteractive: zod.z.boolean().optional().describe("Execute the named setup step without terminal prompts."),
6271
+ answer: zod.z.string().optional().describe(
6272
+ "User selection or input for a non-interactive setup step. Omitted confirmations use the terminal wizard's default."
6273
+ ),
6274
+ flow: zod.z.enum(["login", "signup"]).optional().describe(
6275
+ "Account flow selected for the environment and OAuth callback steps."
6276
+ ),
6277
+ language: zod.z.enum(["javascript", "typescript"]).optional().describe(
6278
+ "Project language for dependency installation and the Slack demo."
6279
+ )
6280
+ }).describe(
6281
+ "Set up the Zapier SDK in an existing or new project. Runs an interactive terminal wizard; AI agents should use --agent instead."
6282
+ );
6283
+
6284
+ // package.json
6285
+ var package_default = {
6286
+ version: "0.87.1"};
6287
+ var agentLabels = {
6288
+ claude: "Claude Code",
6289
+ codex: "Codex",
6290
+ cursor: "Cursor",
6291
+ "vscode-copilot": "VS Code Copilot"
6292
+ };
6293
+ function buildAgentPrompt({
6294
+ context
6295
+ }) {
6296
+ const listConnectionsCommand = formatPackageCommand(
6297
+ createLocalPackageCommand({
6298
+ packageManager: context.packageManager,
6299
+ binary: "zapier-sdk",
6300
+ args: ["list-connections", "--owner", "me", "--json"]
6301
+ })
6302
+ );
6303
+ const listActionsCommand = formatPackageCommand(
6304
+ createLocalPackageCommand({
6305
+ packageManager: context.packageManager,
6306
+ binary: "zapier-sdk",
6307
+ args: ["list-actions", "APP_KEY"]
6308
+ })
6309
+ );
6310
+ return `Start by running this command to discover my active connected apps outside of your sandbox:
6311
+
6312
+ ${listConnectionsCommand}
6313
+
6314
+ Using the connected apps returned by that command:
6315
+
6316
+ - Pick 1-3 apps and show commands I can use to explore their actions, such as: ${listActionsCommand}
6317
+ - Do not run those list-actions commands.
6318
+ - Suggest 3 specific ideas based on my connected apps. Each idea should read from one app, optionally process data, and write to another.
6319
+ - Do not suggest event-driven workflows.
6320
+ - Keep each idea to one sentence. Example: "pull in-progress Jira issues and DM yourself a summary on Slack".
6321
+
6322
+ 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.`;
6178
6323
  }
6179
- var SetupSchema = zod.z.object({}).describe("Set up the Zapier SDK in an existing or new project");
6180
- function printAuthCommand({
6324
+ function printAgentPrompt({
6181
6325
  context,
6182
- flow,
6183
- headless
6326
+ message
6184
6327
  }) {
6185
- const command = createLocalPackageCommand({
6186
- packageManager: context.packageManager,
6187
- binary: "zapier-sdk",
6188
- args: [flow, ...headless ? ["--headless"] : []]
6189
- });
6190
- console.log(`$ ${formatPackageCommand(command)}`);
6191
- if (headless) {
6192
- console.log(
6193
- "Open the printed URL in another browser, then paste the final OAuth callback URL when prompted."
6194
- );
6195
- }
6196
- }
6197
- function isCredentialWriteError(error) {
6198
- const code = typeof error === "object" && error !== null && "code" in error && typeof error.code === "string" ? error.code : "";
6199
- if (/^(eacces|eperm)$/i.test(code)) return true;
6200
- const message = error instanceof Error ? error.message : String(error);
6201
- return /permission|sandbox|eacces|eperm/i.test(message);
6202
- }
6203
- async function confirmAuthentication(context) {
6204
- const { data: profile } = await runWithSetupLoader({
6205
- promise: context.imports.getProfile({}),
6206
- label: "Confirming Zapier account"
6207
- });
6208
- context.accountEmail = profile.email;
6209
- console.log(`Authenticated as ${profile.email}.`);
6210
- }
6211
- async function authenticate(context) {
6212
- const activeProfile = await runWithSetupLoader({
6213
- promise: context.imports.getProfile({}).then(({ data }) => data).catch((error) => {
6214
- if (zapierSdk.isZapierAuthenticationError(error)) return void 0;
6215
- throw error;
6216
- }),
6217
- label: "Checking Zapier authentication"
6218
- });
6219
- const storedCredentialsSelection = await context.selectStoredLoginCredentials();
6220
- if (storedCredentialsSelection === "switch") {
6221
- await confirmAuthentication(context);
6222
- return;
6223
- }
6224
- if (storedCredentialsSelection === "none" && activeProfile) {
6225
- const shouldLogout = await resolveSetupConfirm({
6226
- answer: context.createAnswer(),
6227
- controller: context.setupController,
6228
- message: `You are already logged in as "${activeProfile.email}".
6229
- Logging out will delete these credentials and may interrupt other Zapier SDK or CLI sessions using them.
6230
- Log out and use a different account?`,
6231
- defaultValue: false
6232
- });
6233
- if (!shouldLogout) {
6234
- context.accountEmail = activeProfile.email;
6235
- console.log(`Continuing as ${activeProfile.email}.`);
6236
- return;
6237
- }
6238
- const logoutCommand = createLocalPackageCommand({
6239
- packageManager: context.packageManager,
6240
- binary: "zapier-sdk",
6241
- args: ["logout"]
6242
- });
6243
- console.log(`$ ${formatPackageCommand(logoutCommand)}`);
6244
- await context.imports.logout({});
6245
- }
6246
- const flow = await resolveSetupSelect({
6247
- answer: context.createAnswer(),
6248
- controller: context.setupController,
6249
- message: "Zapier account:",
6250
- choices: [
6251
- { label: "Log in to an existing account", value: "login" },
6252
- { label: "Create a new Zapier account", value: "signup" }
6253
- ]
6254
- });
6255
- const environment = await resolveSetupSelect({
6256
- answer: context.createAnswer(),
6257
- controller: context.setupController,
6258
- message: "Environment:",
6259
- choices: [
6260
- { label: "Local machine with a browser", value: "local" },
6261
- { label: "Remote or headless", value: "headless" }
6262
- ]
6263
- });
6264
- const headless = environment === "headless";
6265
- printAuthCommand({ context, flow, headless });
6266
- try {
6267
- if (storedCredentialsSelection === "create") {
6268
- await context.authenticateNewAccount({ entryPoint: flow, headless });
6269
- } else {
6270
- const runAuth = flow === "login" ? context.imports.login : context.imports.signup;
6271
- await runAuth({ headless });
6272
- }
6273
- } catch (error) {
6274
- if (isCredentialWriteError(error)) {
6275
- console.error(
6276
- "Authentication could not write credentials. A permissions or sandbox restriction may be blocking disk access."
6277
- );
6278
- }
6279
- throw error;
6280
- }
6281
- await confirmAuthentication(context);
6282
- }
6283
- var agentLabels = {
6284
- claude: "Claude Code",
6285
- codex: "Codex",
6286
- cursor: "Cursor",
6287
- "vscode-copilot": "VS Code Copilot"
6288
- };
6289
- function buildAgentPrompt({ context }) {
6290
- const listConnectionsCommand = formatPackageCommand(
6291
- createLocalPackageCommand({
6292
- packageManager: context.packageManager,
6293
- binary: "zapier-sdk",
6294
- args: ["list-connections", "--owner", "me", "--json"]
6295
- })
6296
- );
6297
- const listActionsCommand = formatPackageCommand(
6298
- createLocalPackageCommand({
6299
- packageManager: context.packageManager,
6300
- binary: "zapier-sdk",
6301
- args: ["list-actions", "APP_KEY"]
6302
- })
6303
- );
6304
- return `Start by running this command to discover my active connected apps outside of your sandbox:
6305
-
6306
- ${listConnectionsCommand}
6307
-
6308
- Using the connected apps returned by that command:
6309
-
6310
- - Pick 1-3 apps and show commands I can use to explore their actions, such as: ${listActionsCommand}
6311
- - Do not run those list-actions commands.
6312
- - Suggest 3 specific ideas based on my connected apps. Each idea should read from one app, optionally process data, and write to another.
6313
- - Do not suggest event-driven workflows.
6314
- - Keep each idea to one sentence. Example: "pull in-progress Jira issues and DM yourself a summary on Slack".
6315
-
6316
- 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.`;
6317
- }
6318
- function printAgentPrompt({
6319
- context,
6320
- message
6321
- }) {
6322
- console.log();
6323
- console.log(chalk5__default.default.bgYellow.black.bold(message));
6324
- console.log();
6325
- console.log(buildAgentPrompt({ context }));
6328
+ console.log();
6329
+ console.log(chalk5__default.default.bgYellow.black.bold(message));
6330
+ console.log();
6331
+ console.log(buildAgentPrompt({ context }));
6326
6332
  }
6327
6333
  async function openAgentHandoff(context) {
6328
6334
  const agent = await resolveSetupSelect({
@@ -6393,27 +6399,291 @@ async function openAgentHandoff(context) {
6393
6399
  message: "If it didn't load correctly, paste this prompt into your agent:"
6394
6400
  });
6395
6401
  }
6402
+
6403
+ // src/plugins/setup/agent-output.ts
6404
+ var REQUIRED_STEPS = [
6405
+ "directory",
6406
+ "install"
6407
+ ];
6408
+ function formatSetupCommand({
6409
+ context,
6410
+ target,
6411
+ answer
6412
+ }) {
6413
+ const args = [
6414
+ "setup",
6415
+ "--step",
6416
+ target.step,
6417
+ "--non-interactive",
6418
+ ...target.flow ? ["--flow", target.flow] : [],
6419
+ "--language",
6420
+ context.projectLanguage,
6421
+ ...answer !== void 0 ? ["--answer", answer] : []
6422
+ ];
6423
+ return formatPackageCommand(
6424
+ SOURCE_CLI_PATH ? {
6425
+ command: process.execPath,
6426
+ args: [
6427
+ "--import",
6428
+ module$1.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('experimental.cjs', document.baseURI).href))).resolve("tsx"),
6429
+ "--conditions",
6430
+ "source",
6431
+ SOURCE_CLI_PATH,
6432
+ ...args
6433
+ ]
6434
+ } : createPackageRunnerCommand({
6435
+ packageManager: context.packageManager,
6436
+ packageName: `@zapier/zapier-sdk-cli@${package_default.version}`,
6437
+ args
6438
+ })
6439
+ );
6440
+ }
6441
+ function printAgentStep({
6442
+ context,
6443
+ target,
6444
+ question
6445
+ }) {
6446
+ console.log(
6447
+ `Step: ${target.step}
6448
+ Working directory: ${context.cwd}
6449
+ Detected package manager: ${context.packageManager}
6450
+ Project language: ${context.projectLanguage}`
6451
+ );
6452
+ if (!question) {
6453
+ console.log(
6454
+ `Run this exact command:
6455
+ ${formatSetupCommand({ context, target })}`
6456
+ );
6457
+ } else {
6458
+ if (target.step === "authorize" && context.authorizationUrl)
6459
+ console.log(
6460
+ `Give the user this URL to open in a browser where they can sign in to Zapier:
6461
+ ${context.authorizationUrl}
6462
+ 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.`
6463
+ );
6464
+ console.log(`Ask the user this question verbatim: "${question.message}"`);
6465
+ if (question.type === "select") {
6466
+ for (const { value, label } of question.choices)
6467
+ console.log(`${JSON.stringify(value)}: ${label}`);
6468
+ for (const { value } of question.choices) {
6469
+ const decision = value === "true" ? "agrees" : value === "false" ? "declines" : `chooses ${JSON.stringify(value)}`;
6470
+ if (value === "false" && REQUIRED_STEPS.includes(target.step))
6471
+ console.log("If the user declines, stop setup.");
6472
+ else
6473
+ console.log(
6474
+ `If the user ${decision}, run this exact command:
6475
+ ${formatSetupCommand({ context, target, answer: value })}`
6476
+ );
6477
+ }
6478
+ } else {
6479
+ console.log(
6480
+ `Run this exact command with their answer as one shell-quoted argument (an empty answer is ''):
6481
+ ${formatSetupCommand({ context, target, answer: "<answer>" })}`
6482
+ );
6483
+ }
6484
+ if (target.step === "account-environment")
6485
+ console.log(
6486
+ `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.`
6487
+ );
6488
+ console.log(
6489
+ "Use your tool for asking the user questions if you have one. Do not answer on the user's behalf."
6490
+ );
6491
+ }
6492
+ console.log(
6493
+ "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."
6494
+ );
6495
+ }
6496
+ function printAgentCompletion({
6497
+ context,
6498
+ cancelled = false
6499
+ }) {
6500
+ console.log(
6501
+ cancelled ? "Setup cancelled." : `Setup walkthrough finished. Report which operations succeeded, failed, or were skipped based on their command results.
6502
+ Learn more: https://docs.zapier.com/sdk
6503
+
6504
+ Continue in this agent. Do not open another agent or ask the user to choose one.
6505
+ ${buildAgentPrompt({ context })}`
6506
+ );
6507
+ }
6508
+
6509
+ // src/plugins/setup/authentication.ts
6510
+ function printAuthCommand({
6511
+ context,
6512
+ flow,
6513
+ headless = false
6514
+ }) {
6515
+ const command = createLocalPackageCommand({
6516
+ packageManager: context.packageManager,
6517
+ binary: "zapier-sdk",
6518
+ args: [flow, ...headless ? ["--headless"] : []]
6519
+ });
6520
+ console.log(`$ ${formatPackageCommand(command)}`);
6521
+ if (headless) {
6522
+ console.log(
6523
+ "Open the printed URL in another browser, then paste the final OAuth callback URL when prompted."
6524
+ );
6525
+ }
6526
+ }
6527
+ function isCredentialWriteError(error) {
6528
+ const code = typeof error === "object" && error !== null && "code" in error && typeof error.code === "string" ? error.code : "";
6529
+ if (/^(eacces|eperm)$/i.test(code)) return true;
6530
+ const message = error instanceof Error ? error.message : String(error);
6531
+ return /permission|sandbox|eacces|eperm/i.test(message);
6532
+ }
6533
+ async function confirmAuthentication(context) {
6534
+ const { data: profile } = await context.load({
6535
+ promise: context.imports.getProfile({}),
6536
+ label: "Confirming Zapier account"
6537
+ });
6538
+ context.accountEmail = profile.email;
6539
+ console.log(`Authenticated as ${profile.email}.`);
6540
+ }
6541
+ function selectAccountFlow(context) {
6542
+ return resolveSetupSelect({
6543
+ answer: context.createAnswer(),
6544
+ controller: context.setupController,
6545
+ message: "Zapier account:",
6546
+ choices: [
6547
+ { label: "Log in to an existing account", value: "login" },
6548
+ { label: "Create a new Zapier account", value: "signup" }
6549
+ ]
6550
+ });
6551
+ }
6552
+ function selectAuthenticationEnvironment(context) {
6553
+ return resolveSetupSelect({
6554
+ answer: context.createAnswer(),
6555
+ controller: context.setupController,
6556
+ message: "Environment:",
6557
+ choices: [
6558
+ { label: "Local machine with a browser", value: "local" },
6559
+ { label: "Remote or headless", value: "headless" }
6560
+ ]
6561
+ });
6562
+ }
6563
+ async function selectAccount(context) {
6564
+ const activeProfile = await context.load({
6565
+ promise: context.imports.getProfile({}).then(({ data }) => data).catch((error) => {
6566
+ if (zapierSdk.isZapierAuthenticationError(error)) return void 0;
6567
+ throw error;
6568
+ }),
6569
+ label: "Checking Zapier authentication"
6570
+ });
6571
+ const storedCredentialsSelection = await context.selectStoredLoginCredentials();
6572
+ if (storedCredentialsSelection === "none" && activeProfile) {
6573
+ const shouldLogout = await resolveSetupConfirm({
6574
+ answer: context.createAnswer(),
6575
+ controller: context.setupController,
6576
+ message: `You are already logged in as "${activeProfile.email}".
6577
+ Logging out will delete these credentials and may interrupt other Zapier SDK or CLI sessions using them.
6578
+ Log out and use a different account?`,
6579
+ defaultValue: false
6580
+ });
6581
+ if (!shouldLogout) {
6582
+ context.accountEmail = activeProfile.email;
6583
+ console.log(`Continuing as ${activeProfile.email}.`);
6584
+ return "continue";
6585
+ }
6586
+ printAuthCommand({ context, flow: "logout" });
6587
+ await context.imports.logout({});
6588
+ }
6589
+ return storedCredentialsSelection;
6590
+ }
6591
+ async function authenticate(context) {
6592
+ const next = await runAuthenticationStep({ context, options: {} });
6593
+ if (next.step === "confirm-account") await confirmAuthentication(context);
6594
+ }
6595
+ async function runAuthenticationStep({
6596
+ context,
6597
+ options
6598
+ }) {
6599
+ const { step } = options;
6600
+ let newAccount = step === "account-environment";
6601
+ if (!step || step === "account") {
6602
+ const selection = await selectAccount(context);
6603
+ if (selection === "switch") return { step: "confirm-account" };
6604
+ if (selection === "continue")
6605
+ return { step: step ? "confirm-account" : "connections" };
6606
+ if (step) return { step: "account-flow" };
6607
+ newAccount = selection === "create";
6608
+ }
6609
+ if (step === "confirm-account") {
6610
+ await confirmAuthentication(context);
6611
+ return { step: "connections" };
6612
+ }
6613
+ const flow = !step || step === "account-flow" ? await selectAccountFlow(context) : options.flow;
6614
+ if (step === "account-flow")
6615
+ return {
6616
+ step: "account-environment",
6617
+ flow
6618
+ };
6619
+ if (!flow)
6620
+ throw new ZapierCliValidationError(
6621
+ "This setup step requires --flow login or --flow signup."
6622
+ );
6623
+ let headless = true;
6624
+ let callbackUrl;
6625
+ let recoveryMessage;
6626
+ if (step === "authorize") {
6627
+ recoveryMessage = `Generate a fresh authorization URL with \`${formatSetupCommand(
6628
+ {
6629
+ context,
6630
+ target: { step: "account-environment", flow },
6631
+ answer: "headless"
6632
+ }
6633
+ )}\`.`;
6634
+ const pending = getPendingOauthFlow({ entryPoint: flow, recoveryMessage });
6635
+ context.authorizationUrl = pending.transaction.browserAuthUrl;
6636
+ callbackUrl = await resolveSetupInput({
6637
+ answer: context.createAnswer(),
6638
+ controller: context.setupController,
6639
+ message: OAUTH_CALLBACK_QUESTION
6640
+ });
6641
+ } else {
6642
+ headless = await selectAuthenticationEnvironment(context) === "headless";
6643
+ if (!step) printAuthCommand({ context, flow, headless });
6644
+ }
6645
+ const authOptions = {
6646
+ headless,
6647
+ ...step ? { nonInteractive: true } : {},
6648
+ ...callbackUrl !== void 0 ? { callbackUrl } : {}
6649
+ };
6650
+ try {
6651
+ if (newAccount) {
6652
+ await context.authenticateNewAccount({
6653
+ entryPoint: flow,
6654
+ ...authOptions
6655
+ });
6656
+ } else {
6657
+ await context.imports[flow](authOptions);
6658
+ }
6659
+ } catch (error) {
6660
+ if (recoveryMessage) console.error(recoveryMessage);
6661
+ if (isCredentialWriteError(error)) {
6662
+ console.error(
6663
+ "Authentication could not write credentials. A permissions or sandbox restriction may be blocking disk access."
6664
+ );
6665
+ }
6666
+ throw error;
6667
+ }
6668
+ return step === "account-environment" && headless ? { step: "authorize", flow } : { step: "confirm-account" };
6669
+ }
6396
6670
  var CONNECTIONS_URL = "https://zapier.com/app/assets/connections";
6397
6671
  var CONNECTION_DISPLAY_LIMIT = 10;
6398
- async function ensureConnectionsAvailable(context) {
6399
- const page = await runWithSetupLoader({
6672
+ async function showActiveConnections(context) {
6673
+ const page = await context.load({
6400
6674
  promise: context.imports.listConnections({
6401
6675
  owner: "me",
6402
6676
  maxItems: CONNECTION_DISPLAY_LIMIT
6403
6677
  }),
6404
6678
  label: "Loading app connections"
6405
6679
  });
6406
- if (page.data.length === 0) {
6407
- console.log(
6408
- `No active connections found. Connect or reconnect an app at ${CONNECTIONS_URL}, then come back and run setup again.`
6409
- );
6410
- throw new ZapierCliExitError(
6411
- "Setup stopped before completion because no active connections are available."
6412
- );
6413
- }
6680
+ if (page.data.length === 0) return false;
6414
6681
  const table = new Table__default.default({
6415
6682
  head: ["App", "Connection"],
6416
- style: { compact: true }
6683
+ style: {
6684
+ compact: true,
6685
+ ...process.stdout.isTTY ? {} : { head: [], border: [] }
6686
+ }
6417
6687
  });
6418
6688
  table.push(
6419
6689
  ...page.data.map((connection) => [
@@ -6425,11 +6695,22 @@ async function ensureConnectionsAvailable(context) {
6425
6695
  `Active Zapier connections (showing up to ${CONNECTION_DISPLAY_LIMIT}):`
6426
6696
  );
6427
6697
  console.log(table.toString());
6698
+ return true;
6699
+ }
6700
+ async function ensureConnectionsAvailable(context) {
6701
+ if (await showActiveConnections(context)) return;
6702
+ console.log(
6703
+ `No active connections found. Connect or reconnect an app at ${CONNECTIONS_URL}, then come back and run setup again.`
6704
+ );
6705
+ throw new ZapierCliExitError(
6706
+ "Setup stopped before completion because no active connections are available."
6707
+ );
6428
6708
  }
6429
6709
  var CONNECTION_PLACEHOLDER = "__ZAPIER_SDK_SLACK_CONNECTION__";
6430
6710
  var EMAIL_PLACEHOLDER = "__ZAPIER_SDK_ACCOUNT_EMAIL__";
6431
6711
  var APP_PLACEHOLDER = "__ZAPIER_SDK_SLACK_APP__";
6432
6712
  var SLACK_SLUG = "slack";
6713
+ var SLACK_RETRY_QUESTION = "Slack email for one retry after the failed test:";
6433
6714
  function getSlackTestPath(context) {
6434
6715
  const extension = context.projectLanguage === "typescript" ? "ts" : "mjs";
6435
6716
  return path.join("src", "sdk-demo", `zapier-slack-test.${extension}`);
@@ -6439,7 +6720,7 @@ function getSlackTestTemplatePath(context) {
6439
6720
  return path.join(TEMPLATES_DIR, "setup", `zapier-slack-test.${extension}`);
6440
6721
  }
6441
6722
  async function findSlackConnection(context) {
6442
- const page = await runWithSetupLoader({
6723
+ const page = await context.load({
6443
6724
  promise: context.imports.listConnections({
6444
6725
  owner: "me",
6445
6726
  app: SLACK_SLUG,
@@ -6460,17 +6741,22 @@ function renderSlackTest({
6460
6741
  }) {
6461
6742
  return fs.readFileSync(getSlackTestTemplatePath(context), "utf8").replace(APP_PLACEHOLDER, () => toTemplateString(app)).replace(CONNECTION_PLACEHOLDER, () => toTemplateString(connection)).replace(EMAIL_PLACEHOLDER, () => toTemplateString(email));
6462
6743
  }
6463
- function writeSlackTest({
6744
+ async function createAndRunSlackTest({
6464
6745
  context,
6465
- app,
6466
6746
  connection,
6467
- email
6747
+ email,
6748
+ overwrite = true
6468
6749
  }) {
6469
6750
  const slackTestPath = getSlackTestPath(context);
6470
6751
  const path2 = path.join(context.cwd, slackTestPath);
6471
6752
  if (!fs.existsSync(path.dirname(path2))) fs.mkdirSync(path.dirname(path2), { recursive: true });
6472
- fs.writeFileSync(path2, renderSlackTest({ context, app, connection, email }));
6753
+ fs.writeFileSync(
6754
+ path2,
6755
+ renderSlackTest({ context, app: SLACK_SLUG, connection, email }),
6756
+ { flag: overwrite ? "w" : "wx" }
6757
+ );
6473
6758
  console.log(`Created ${slackTestPath}.`);
6759
+ return trySlackTest(context);
6474
6760
  }
6475
6761
  async function executeSlackTest(context) {
6476
6762
  const slackTestPath = getSlackTestPath(context);
@@ -6498,75 +6784,76 @@ async function executeSlackTest(context) {
6498
6784
  async function trySlackTest(context) {
6499
6785
  try {
6500
6786
  await executeSlackTest(context);
6787
+ (context.outcomes ?? (context.outcomes = {})).slack = "succeeded";
6501
6788
  return true;
6502
6789
  } catch (error) {
6790
+ (context.outcomes ?? (context.outcomes = {})).slack = "failed";
6503
6791
  const message = error instanceof Error ? error.message : String(error);
6504
6792
  console.warn(`Slack demo failed: ${message}. Setup will continue.`);
6505
6793
  return false;
6506
6794
  }
6507
6795
  }
6508
- async function offerSlackTest(context) {
6796
+ async function runSlackStep({
6797
+ context,
6798
+ step,
6799
+ connection
6800
+ }) {
6509
6801
  if (!context.accountEmail) {
6510
6802
  console.log("No account email found; skipping the Slack demo.");
6511
- return;
6803
+ return "handoff";
6512
6804
  }
6513
- const slackConnection = await findSlackConnection(context);
6514
- if (!slackConnection) {
6805
+ if (!connection) {
6515
6806
  console.log("No active Slack connection found; skipping the Slack demo.");
6516
- return;
6807
+ return "handoff";
6517
6808
  }
6518
- const slackTestPath = getSlackTestPath(context);
6519
- const shouldRun = await resolveSetupConfirm({
6520
- answer: context.createAnswer(),
6521
- controller: context.setupController,
6522
- message: `Run the Slack self-DM test using ${context.accountEmail}?`,
6523
- defaultValue: true
6524
- });
6525
- if (!shouldRun) return;
6526
- const path2 = path.join(context.cwd, slackTestPath);
6527
- if (fs.existsSync(path2)) {
6528
- const shouldOverwrite = await resolveSetupConfirm({
6809
+ if (step === "slack-retry") {
6810
+ const email = await resolveSetupInput({
6529
6811
  answer: context.createAnswer(),
6530
6812
  controller: context.setupController,
6531
- message: `${slackTestPath} already exists. Is it okay to overwrite it?`,
6532
- defaultValue: false
6813
+ message: SLACK_RETRY_QUESTION
6533
6814
  });
6534
- if (!shouldOverwrite) {
6535
- console.log("Keeping the existing Slack test file.");
6536
- const shouldRunExisting = await resolveSetupConfirm({
6537
- answer: context.createAnswer(),
6538
- controller: context.setupController,
6539
- message: `Run the existing ${slackTestPath}?`,
6540
- defaultValue: false
6815
+ if (email)
6816
+ await createAndRunSlackTest({
6817
+ context,
6818
+ connection: connection.id,
6819
+ email
6541
6820
  });
6542
- if (shouldRunExisting) await trySlackTest(context);
6543
- return;
6544
- }
6821
+ else console.log("Skipping the Slack test.");
6822
+ return "handoff";
6545
6823
  }
6546
- let email = context.accountEmail;
6547
- writeSlackTest({
6548
- context,
6549
- app: SLACK_SLUG,
6550
- connection: slackConnection.id,
6551
- email
6552
- });
6553
- if (await trySlackTest(context)) return;
6554
- email = await resolveSetupInput({
6824
+ const slackTestPath = getSlackTestPath(context);
6825
+ const shouldRun = await resolveSetupConfirm({
6555
6826
  answer: context.createAnswer(),
6556
6827
  controller: context.setupController,
6557
- message: "Slack email for one retry after the failed test:"
6828
+ 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}?`,
6829
+ defaultValue: step === "slack"
6558
6830
  });
6559
- if (!email) {
6560
- console.log("Skipping the Slack test.");
6561
- return;
6831
+ if (!shouldRun) {
6832
+ if (step === "slack-overwrite") {
6833
+ console.log("Keeping the existing Slack test file.");
6834
+ return "slack-existing";
6835
+ }
6836
+ return "handoff";
6562
6837
  }
6563
- writeSlackTest({
6838
+ if (step === "slack" && fs.existsSync(path.join(context.cwd, slackTestPath)))
6839
+ return "slack-overwrite";
6840
+ if (step === "slack-existing") {
6841
+ await trySlackTest(context);
6842
+ return "handoff";
6843
+ }
6844
+ const succeeded = await createAndRunSlackTest({
6564
6845
  context,
6565
- app: SLACK_SLUG,
6566
- connection: slackConnection.id,
6567
- email
6846
+ connection: connection.id,
6847
+ email: context.accountEmail,
6848
+ overwrite: step === "slack-overwrite"
6568
6849
  });
6569
- await trySlackTest(context);
6850
+ return succeeded ? "handoff" : "slack-retry";
6851
+ }
6852
+ async function offerSlackTest(context) {
6853
+ const connection = context.accountEmail ? await findSlackConnection(context) : void 0;
6854
+ let step = "slack";
6855
+ while (step !== "handoff")
6856
+ step = await runSlackStep({ context, step, connection });
6570
6857
  }
6571
6858
 
6572
6859
  // src/plugins/setup/skill.ts
@@ -6592,7 +6879,9 @@ async function installOptionalSkill(context) {
6592
6879
  cwd: context.cwd,
6593
6880
  capture: true
6594
6881
  });
6882
+ (context.outcomes ?? (context.outcomes = {})).skill = "succeeded";
6595
6883
  } catch {
6884
+ (context.outcomes ?? (context.outcomes = {})).skill = "failed";
6596
6885
  console.warn(
6597
6886
  `Could not install the optional Zapier SDK skill. To install it manually, run: ${formatPackageCommand(command)}`
6598
6887
  );
@@ -6649,6 +6938,177 @@ async function runWizard(context) {
6649
6938
  console.log(chalk5__default.default.bgCyan.black.bold(" Next steps "));
6650
6939
  await openAgentHandoff(context);
6651
6940
  }
6941
+ var UNPROMPTED_STEPS = [
6942
+ "project",
6943
+ "confirm-account",
6944
+ "connections"
6945
+ ];
6946
+ var ZapierSetupQuestionSignal = class extends zapierSdk.ZapierSignal {
6947
+ constructor(question) {
6948
+ super(question.message);
6949
+ this.question = question;
6950
+ this.name = "ZapierSetupQuestionSignal";
6951
+ this.code = "ZAPIER_SETUP_QUESTION";
6952
+ }
6953
+ };
6954
+ function isZapierSetupQuestionSignal(error) {
6955
+ return zapierSdk.isZapierSignal(error) && error.code === "ZAPIER_SETUP_QUESTION";
6956
+ }
6957
+ function createSetupAnswer({
6958
+ answer,
6959
+ execute
6960
+ }) {
6961
+ let answered = false;
6962
+ return async ({ result: { question } }) => {
6963
+ if (question.type === "collection")
6964
+ throw new ZapierCliValidationError(
6965
+ "Setup expects a selection or text input."
6966
+ );
6967
+ if (!execute) throw new ZapierSetupQuestionSignal(question);
6968
+ if (answered)
6969
+ throw new ZapierCliValidationError(
6970
+ "Each setup step can answer only one question. Inspect the next question with --agent and run it in a separate command."
6971
+ );
6972
+ answered = true;
6973
+ const confirmation = question.type === "select" && question.choices.some(({ value }) => value === "true") && question.choices.some(({ value }) => value === "false");
6974
+ if (answer === void 0 && !confirmation)
6975
+ throw new ZapierCliValidationError(
6976
+ "This setup step requires --answer. Inspect it with --agent and pass the user's selection or input."
6977
+ );
6978
+ if (question.type === "input")
6979
+ return { type: "custom", value: answer?.trim() ?? "" };
6980
+ const selected = answer?.trim() ?? question.choices[0].value;
6981
+ if (!question.choices.some(({ value }) => value === selected))
6982
+ throw new ZapierCliValidationError(
6983
+ `Answer must be one of: ${question.choices.map(({ value }) => value).join(", ")}.`
6984
+ );
6985
+ return { type: "choose", value: selected };
6986
+ };
6987
+ }
6988
+ async function runSetupStep({
6989
+ context,
6990
+ target
6991
+ }) {
6992
+ switch (target.step) {
6993
+ case "directory":
6994
+ await chooseDirectory(context);
6995
+ return { step: "project" };
6996
+ case "project":
6997
+ await ensureSupportedNode(context);
6998
+ await initializeProject(context);
6999
+ return { step: "install" };
7000
+ case "install":
7001
+ await offerMissingPackageInstall({
7002
+ context,
7003
+ packages: getPackageStates(context).filter(({ declared }) => !declared)
7004
+ });
7005
+ return { step: "sdk-update" };
7006
+ case "sdk-update":
7007
+ case "cli-update": {
7008
+ const name = target.step === "sdk-update" ? "@zapier/zapier-sdk" : "@zapier/zapier-sdk-cli";
7009
+ const packageState = getPackageStates(context).find(
7010
+ ({ dependency }) => dependency.name === name
7011
+ );
7012
+ if (packageState) await offerPackageUpdate({ context, packageState });
7013
+ return { step: target.step === "sdk-update" ? "cli-update" : "skill" };
7014
+ }
7015
+ case "skill":
7016
+ await installOptionalSkill(context);
7017
+ return { step: "account" };
7018
+ case "account":
7019
+ case "account-flow":
7020
+ case "account-environment":
7021
+ case "authorize":
7022
+ case "confirm-account":
7023
+ return runAuthenticationStep({ context, options: target });
7024
+ case "connections":
7025
+ if (!await showActiveConnections(context)) {
7026
+ console.log(
7027
+ `No active connections found. Ask the user to connect or reconnect an app at ${CONNECTIONS_URL}. After they confirm, run this exact command:
7028
+ ${formatSetupCommand({ context, target })}
7029
+ Success means it prints the active connections table.`
7030
+ );
7031
+ throw new ZapierCliExitError(
7032
+ "Setup stopped until an app connection is active."
7033
+ );
7034
+ }
7035
+ console.log(
7036
+ "Show the user every app and connection title in the table above before continuing. Do not just report that connections were found."
7037
+ );
7038
+ return { step: "slack" };
7039
+ case "slack":
7040
+ case "slack-overwrite":
7041
+ case "slack-existing":
7042
+ case "slack-retry": {
7043
+ const { data: profile } = await context.imports.getProfile({});
7044
+ context.accountEmail = profile.email;
7045
+ return {
7046
+ step: await runSlackStep({
7047
+ context,
7048
+ step: target.step,
7049
+ connection: await findSlackConnection(context)
7050
+ })
7051
+ };
7052
+ }
7053
+ }
7054
+ }
7055
+ async function runAgentSetup({
7056
+ context,
7057
+ options
7058
+ }) {
7059
+ if (options.agent && options.nonInteractive)
7060
+ throw new ZapierCliValidationError(
7061
+ "Use --agent to inspect a step or --non-interactive to execute it, not both."
7062
+ );
7063
+ if (!options.agent && !options.nonInteractive)
7064
+ throw new ZapierCliValidationError(
7065
+ options.step ? "Use --agent to inspect the named step or --non-interactive to execute it." : "--answer, --flow, and --language require a setup step."
7066
+ );
7067
+ if (options.nonInteractive && !options.step)
7068
+ throw new ZapierCliValidationError(
7069
+ "--non-interactive requires --step. Start with setup --agent and follow its printed commands."
7070
+ );
7071
+ if (options.answer !== void 0 && !options.nonInteractive)
7072
+ throw new ZapierCliValidationError("--answer requires --non-interactive.");
7073
+ context.cwd = fs.realpathSync(context.cwd);
7074
+ context.projectLanguage = options.language ?? detectProjectLanguage({
7075
+ cwd: context.cwd,
7076
+ hasExistingProject: fs.existsSync(path.join(context.cwd, "package.json"))
7077
+ });
7078
+ let target = {
7079
+ step: options.step ?? "directory",
7080
+ flow: options.flow
7081
+ };
7082
+ let execute = options.nonInteractive === true;
7083
+ while (true) {
7084
+ const { step, flow } = target;
7085
+ if (step === "handoff") return printAgentCompletion({ context });
7086
+ if (!execute && UNPROMPTED_STEPS.includes(step))
7087
+ return printAgentStep({ context, target });
7088
+ const answer = createSetupAnswer({
7089
+ answer: execute ? options.answer : void 0,
7090
+ execute
7091
+ });
7092
+ context.createAnswer = () => answer;
7093
+ try {
7094
+ const next = await runSetupStep({ context, target: { step, flow } });
7095
+ if (options.language) context.projectLanguage = options.language;
7096
+ if (execute)
7097
+ for (const [operation, outcome] of Object.entries(
7098
+ context.outcomes ?? {}
7099
+ ))
7100
+ console.log(`${operation}: ${outcome}`);
7101
+ target = next;
7102
+ execute = false;
7103
+ } catch (error) {
7104
+ if (isZapierSetupQuestionSignal(error))
7105
+ return printAgentStep({ context, target, question: error.question });
7106
+ if (error instanceof ZapierCliUserCancellationError)
7107
+ return printAgentCompletion({ context, cancelled: true });
7108
+ throw error;
7109
+ }
7110
+ }
7111
+ }
6652
7112
 
6653
7113
  // src/plugins/setup/index.ts
6654
7114
  var getProfileRef = zapierSdk.declareMethod({ id: "getProfile" });
@@ -6673,10 +7133,19 @@ var setupPlugin = zapierSdk.defineMethod({
6673
7133
  supportsJsonOutput: false,
6674
7134
  inputSchema: SetupSchema,
6675
7135
  output: "raw",
6676
- run: async ({ imports }) => {
7136
+ run: async ({ imports, input }) => {
7137
+ if (input.agent || input.nonInteractive || [input.step, input.answer, input.flow, input.language].some(
7138
+ (value) => value !== void 0
7139
+ )) {
7140
+ await runAgentSetup({
7141
+ context: createWizardContext({ imports, agent: true }),
7142
+ options: input
7143
+ });
7144
+ return;
7145
+ }
6677
7146
  if (process.stdin.isTTY !== true || process.stdout.isTTY !== true) {
6678
- throw new ZapierCliExitError(
6679
- "`zapier-sdk setup` requires an interactive terminal."
7147
+ throw new ZapierCliValidationError(
7148
+ "`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."
6680
7149
  );
6681
7150
  }
6682
7151
  await runWizard(createWizardContext({ imports }));
@@ -7171,39 +7640,6 @@ var watchTriggerInboxCliPlugin = zapierSdk.defineMethod({
7171
7640
  return doWatch();
7172
7641
  }
7173
7642
  });
7174
- var createWorkflowRef = experimental.declareMethod({ id: "createWorkflow" });
7175
- function createPublicInputSchema(schema) {
7176
- return zod.z.object(
7177
- Object.fromEntries(
7178
- Object.entries(schema.shape).filter(
7179
- ([name]) => !experimental.INTERNAL_CREATE_WORKFLOW_FIELDS.includes(
7180
- name
7181
- )
7182
- )
7183
- )
7184
- ).describe(schema.description ?? "Create a workflow");
7185
- }
7186
- var CreateWorkflowCliOptionsSchema = createPublicInputSchema(
7187
- experimental.CreateWorkflowOptionsSchema
7188
- );
7189
- var createWorkflowCliPlugin = experimental.defineMethod({
7190
- namespace: "cli",
7191
- name: "createWorkflow",
7192
- imports: [createWorkflowRef],
7193
- type: "create",
7194
- itemType: "Workflow",
7195
- categories: ["code-workflow"],
7196
- stability: "experimental",
7197
- inputSchema: CreateWorkflowCliOptionsSchema,
7198
- outputSchema: experimental.CreateWorkflowResponseSchema,
7199
- skipOutputValidation: true,
7200
- output: "raw",
7201
- run: ({ imports, input }) => imports.createWorkflow({
7202
- ...input,
7203
- creationHarness: experimental.getAgent(),
7204
- creationAccessMethod: "sdk_cli"
7205
- })
7206
- });
7207
7643
  function warningBlock({
7208
7644
  banner,
7209
7645
  message,
@@ -7229,9 +7665,9 @@ function collectDeprecationNoticeAndForward(event, onEvent) {
7229
7665
  }
7230
7666
 
7231
7667
  // package.json with { type: 'json' }
7232
- var package_default = {
7668
+ var package_default2 = {
7233
7669
  name: "@zapier/zapier-sdk-cli",
7234
- version: "0.86.6"};
7670
+ version: "0.87.1"};
7235
7671
 
7236
7672
  // src/sdk.ts
7237
7673
  var warnedDeprecatedMethods = /* @__PURE__ */ new Set();
@@ -7300,7 +7736,7 @@ function createZapierCliSdk(options = {}) {
7300
7736
  const stackOptions = {
7301
7737
  ...sdkOptions,
7302
7738
  eventEmission: { ...sdkOptions.eventEmission, callContext: "cli" },
7303
- callerPackage: { name: package_default.name, version: package_default.version },
7739
+ callerPackage: { name: package_default2.name, version: package_default2.version },
7304
7740
  // Collected for the boxed stderr warning rendered in the exit path. The
7305
7741
  // box is additive: the SDK's own inline warn still fires, so the notice
7306
7742
  // cannot be hidden by any rendering path.
@@ -7312,12 +7748,10 @@ function createZapierCliSdk(options = {}) {
7312
7748
  exports: [
7313
7749
  experimental.omitExports(experimental.zapierExperimentalSdkPlugin, [
7314
7750
  "drainTriggerInbox",
7315
- "watchTriggerInbox",
7316
- "createWorkflow"
7751
+ "watchTriggerInbox"
7317
7752
  ]),
7318
7753
  drainTriggerInboxCliPlugin,
7319
7754
  watchTriggerInboxCliPlugin,
7320
- createWorkflowCliPlugin,
7321
7755
  loginPlugin,
7322
7756
  signupPlugin,
7323
7757
  logoutPlugin,