@zapier/zapier-sdk-cli 0.86.7 → 0.87.2

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/dist/index.cjs CHANGED
@@ -30,6 +30,7 @@ var packageJsonLib = require('package-json');
30
30
  var semver = require('semver');
31
31
  var crossSpawn = require('cross-spawn');
32
32
  var wrapAnsi = require('wrap-ansi');
33
+ var module$1 = require('module');
33
34
  var Table = require('cli-table3');
34
35
  var experimental = require('@zapier/zapier-sdk/experimental');
35
36
  require('commander');
@@ -1052,6 +1053,7 @@ var SENSITIVE_FLAGS = [
1052
1053
  "--credentials-client-id",
1053
1054
  "--credentials-base-url",
1054
1055
  "--callback-url",
1056
+ "--answer",
1055
1057
  "--user",
1056
1058
  "--header",
1057
1059
  "-H",
@@ -1288,6 +1290,7 @@ var searchSelect = core.createPrompt(
1288
1290
  );
1289
1291
 
1290
1292
  // src/utils/auth/credentials-picker.ts
1293
+ var CREDENTIALS_PICKER_QUESTION = "Select credentials or add a new account:";
1291
1294
  function isSameCredentials(left, right) {
1292
1295
  return left.name === right.name && left.baseUrl === right.baseUrl;
1293
1296
  }
@@ -1393,7 +1396,7 @@ async function promptForCredentials({
1393
1396
  activeCredentials
1394
1397
  }) {
1395
1398
  return searchSelect({
1396
- message: "Select credentials or add a new account:",
1399
+ message: CREDENTIALS_PICKER_QUESTION,
1397
1400
  source: (term) => buildCredentialsChoices({
1398
1401
  credentials,
1399
1402
  activeCredentials,
@@ -1456,14 +1459,15 @@ async function switchCredentials({
1456
1459
  async function chooseStoredLoginCredentialsAtBaseUrl({
1457
1460
  baseUrl,
1458
1461
  options,
1459
- resolvedCredentials
1462
+ resolvedCredentials,
1463
+ prompt = promptForCredentials
1460
1464
  }) {
1461
1465
  if (resolvedCredentials !== void 0 || authFlowOnlyOptions(options).length > 0) {
1462
1466
  return "none";
1463
1467
  }
1464
1468
  const storedCredentials = listStoredCredentials({ baseUrl });
1465
1469
  if (storedCredentials.length === 0) return "none";
1466
- const selection = await promptForCredentials({
1470
+ const selection = await prompt({
1467
1471
  credentials: storedCredentials,
1468
1472
  activeCredentials: getActiveCredentials({ baseUrl })
1469
1473
  });
@@ -1477,7 +1481,8 @@ async function chooseStoredLoginCredentialsAtBaseUrl({
1477
1481
  }
1478
1482
  async function chooseStoredLoginCredentials({
1479
1483
  imports,
1480
- options
1484
+ options,
1485
+ prompt
1481
1486
  }) {
1482
1487
  const resolvedCredentials = await imports.resolveCredentials();
1483
1488
  const credentialsBaseUrl = await resolveCredentialsBaseUrl({
@@ -1487,7 +1492,8 @@ async function chooseStoredLoginCredentials({
1487
1492
  return chooseStoredLoginCredentialsAtBaseUrl({
1488
1493
  baseUrl: credentialsBaseUrl,
1489
1494
  options,
1490
- resolvedCredentials
1495
+ resolvedCredentials,
1496
+ prompt
1491
1497
  });
1492
1498
  }
1493
1499
  async function confirmNewCredentialsName(name) {
@@ -1858,6 +1864,7 @@ var PendingOauthTransactionSchema = zod.z.object({
1858
1864
  credentialName: zod.z.string().optional(),
1859
1865
  useApprovals: zod.z.boolean(),
1860
1866
  headless: zod.z.boolean().default(false),
1867
+ preserveExistingCredentials: zod.z.boolean().default(false),
1861
1868
  transaction: OauthTransactionSchema
1862
1869
  });
1863
1870
  function savePendingOauthTransaction({
@@ -1867,7 +1874,8 @@ function savePendingOauthTransaction({
1867
1874
  credentialsBaseUrl,
1868
1875
  credentialName,
1869
1876
  useApprovals,
1870
- headless
1877
+ headless,
1878
+ preserveExistingCredentials = false
1871
1879
  }) {
1872
1880
  const pendingTransaction = {
1873
1881
  version: 1,
@@ -1875,6 +1883,7 @@ function savePendingOauthTransaction({
1875
1883
  expiresAt,
1876
1884
  useApprovals,
1877
1885
  headless,
1886
+ preserveExistingCredentials,
1878
1887
  transaction,
1879
1888
  ...credentialsBaseUrl !== void 0 && { credentialsBaseUrl },
1880
1889
  ...credentialName !== void 0 && { credentialName }
@@ -1893,6 +1902,7 @@ function clearPendingOauthTransaction() {
1893
1902
  }
1894
1903
 
1895
1904
  // src/utils/auth/oauth-flow.ts
1905
+ var OAUTH_CALLBACK_QUESTION = "Paste the final OAuth callback URL:";
1896
1906
  var PENDING_OAUTH_RECOVERY_MESSAGE = "Start a new non-interactive login or signup flow to generate a fresh URL.";
1897
1907
  var LOGIN_OAUTH_COPY = {
1898
1908
  flowName: "Login",
@@ -1956,7 +1966,9 @@ async function startPendingOauthFlow({
1956
1966
  entryPoint,
1957
1967
  credentialName,
1958
1968
  useApprovals,
1959
- headless
1969
+ headless,
1970
+ preserveExistingCredentials = false,
1971
+ silent = false
1960
1972
  }) {
1961
1973
  const copy = entryPoint === "signup" ? SIGNUP_OAUTH_COPY : LOGIN_OAUTH_COPY;
1962
1974
  const port = LOGIN_PORTS[0];
@@ -1973,8 +1985,10 @@ async function startPendingOauthFlow({
1973
1985
  credentialsBaseUrl: baseUrl,
1974
1986
  credentialName,
1975
1987
  useApprovals,
1976
- headless
1988
+ headless,
1989
+ preserveExistingCredentials
1977
1990
  });
1991
+ if (silent) return pending;
1978
1992
  process.stderr.write(
1979
1993
  `Open this ${copy.urlLabel} in a browser to continue:
1980
1994
  `
@@ -2000,23 +2014,24 @@ async function startPendingOauthFlow({
2000
2014
  return pending;
2001
2015
  }
2002
2016
  function getPendingOauthFlow({
2003
- entryPoint
2017
+ entryPoint,
2018
+ recoveryMessage = PENDING_OAUTH_RECOVERY_MESSAGE
2004
2019
  }) {
2005
2020
  const pending = getPendingOauthTransaction();
2006
2021
  if (!pending) {
2007
2022
  throw new ZapierCliValidationError(
2008
- `No pending ${entryPoint} OAuth flow found. ${PENDING_OAUTH_RECOVERY_MESSAGE}`
2023
+ `No pending ${entryPoint} OAuth flow found. ${recoveryMessage}`
2009
2024
  );
2010
2025
  }
2011
2026
  if (Date.now() > pending.expiresAt) {
2012
2027
  clearPendingOauthTransaction();
2013
2028
  throw new ZapierCliValidationError(
2014
- `Pending ${pending.entryPoint} OAuth flow has expired. ${PENDING_OAUTH_RECOVERY_MESSAGE}`
2029
+ `Pending ${pending.entryPoint} OAuth flow has expired. ${recoveryMessage}`
2015
2030
  );
2016
2031
  }
2017
2032
  if (pending.entryPoint !== entryPoint) {
2018
2033
  throw new ZapierCliValidationError(
2019
- `Pending OAuth flow is for ${pending.entryPoint}, not ${entryPoint}. ${PENDING_OAUTH_RECOVERY_MESSAGE}`
2034
+ `Pending OAuth flow is for ${pending.entryPoint}, not ${entryPoint}. ${recoveryMessage}`
2020
2035
  );
2021
2036
  }
2022
2037
  return pending;
@@ -2179,7 +2194,7 @@ async function readHeadlessCallbackUrl({
2179
2194
  process.once("SIGINT", handleCancellation);
2180
2195
  process.once("SIGTERM", handleCancellation);
2181
2196
  if (interactive) {
2182
- void rl.question("Paste the final OAuth callback URL: ", {
2197
+ void rl.question(`${OAUTH_CALLBACK_QUESTION} `, {
2183
2198
  signal: abortController.signal
2184
2199
  }).then(settleResolve).catch((error) => {
2185
2200
  if (error instanceof Error && error.name === "AbortError") {
@@ -2666,7 +2681,7 @@ async function runOauthForEntryPoint({
2666
2681
  if (event.type === "callback_accepted") {
2667
2682
  emitSignupSuccess({
2668
2683
  imports,
2669
- isNonInteractive: false,
2684
+ isNonInteractive: !interactive,
2670
2685
  isHeadless: headless === true
2671
2686
  });
2672
2687
  }
@@ -2748,7 +2763,9 @@ async function runAccountAuth({
2748
2763
  options,
2749
2764
  entryPoint,
2750
2765
  storedCredentialsMode = "choose",
2751
- preserveExistingCredentials = false
2766
+ preserveExistingCredentials = false,
2767
+ useBrowserCallback = false,
2768
+ silent = false
2752
2769
  }) {
2753
2770
  if (options.callbackUrl !== void 0) {
2754
2771
  const { callbackUrl } = options;
@@ -2781,7 +2798,8 @@ async function runAccountAuth({
2781
2798
  imports,
2782
2799
  baseUrl: pending.credentialsBaseUrl,
2783
2800
  interactive: false,
2784
- entryPoint
2801
+ entryPoint,
2802
+ preserveExistingCredentials: pending.preserveExistingCredentials
2785
2803
  })) {
2786
2804
  return;
2787
2805
  }
@@ -2858,7 +2876,7 @@ async function runAccountAuth({
2858
2876
  );
2859
2877
  }
2860
2878
  }
2861
- if (entryPoint === "login" && !interactive && providedName === void 0) {
2879
+ if (entryPoint === "login" && !interactive && providedName === void 0 && !preserveExistingCredentials) {
2862
2880
  const activeCredentials = getActiveCredentials({
2863
2881
  baseUrl: credentialsBaseUrl
2864
2882
  });
@@ -2875,7 +2893,7 @@ async function runAccountAuth({
2875
2893
  }
2876
2894
  const timeoutSeconds = parseTimeoutSeconds(options.timeout);
2877
2895
  const useApprovals = options.useApprovals === true;
2878
- if (!interactive) {
2896
+ if (!interactive && (headless || !useBrowserCallback)) {
2879
2897
  await startPendingOauthFlow({
2880
2898
  entryPoint,
2881
2899
  timeoutMs: timeoutSeconds * 1e3,
@@ -2883,7 +2901,9 @@ async function runAccountAuth({
2883
2901
  baseUrl: credentialsBaseUrl,
2884
2902
  credentialName: providedName,
2885
2903
  useApprovals,
2886
- headless
2904
+ headless,
2905
+ preserveExistingCredentials,
2906
+ silent
2887
2907
  });
2888
2908
  return;
2889
2909
  }
@@ -2904,7 +2924,7 @@ async function runAccountAuth({
2904
2924
  credentialName: providedName,
2905
2925
  interactive,
2906
2926
  useApprovals,
2907
- isNonInteractive: false,
2927
+ isNonInteractive: !interactive,
2908
2928
  isHeadless: headless
2909
2929
  });
2910
2930
  }
@@ -4795,6 +4815,7 @@ function createProjectDir({
4795
4815
  var TEMPLATES_DIR = url.fileURLToPath(
4796
4816
  new URL("../templates", (typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index.cjs', document.baseURI).href)))
4797
4817
  );
4818
+ var SOURCE_CLI_PATH = (typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index.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('index.cjs', document.baseURI).href)))) : void 0;
4798
4819
 
4799
4820
  // src/plugins/init/steps.ts
4800
4821
  var DEFAULT_TEMPLATE = "basic";
@@ -5505,84 +5526,6 @@ async function checkForUpdates({
5505
5526
  };
5506
5527
  }
5507
5528
  }
5508
- var BRAILLE_BLANK = "\u2800";
5509
- var LIGHTNING_INTERVAL_MILLISECONDS = 140;
5510
- var boltRows = [
5511
- "\u2800\u2800\u2880\u28FE\u2800\u2800\u2800\u2800",
5512
- "\u2800\u2800\u28E0\u28FF\u28FF\u2800\u2800\u2800",
5513
- "\u2800\u28FC\u28FF\u28FF\u28FF\u28C0\u28C0\u2840",
5514
- "\u2808\u2809\u2809\u28FF\u28FF\u28FF\u285F\u2800",
5515
- "\u2800\u2800\u2800\u28FF\u28FF\u280B\u2800\u2800",
5516
- "\u2800\u2800\u2800\u287F\u2801\u2800\u2800\u2800"
5517
- ];
5518
- var boltFillOrder = boltRows.flatMap(
5519
- (row, rowIndex) => [...row].flatMap(
5520
- (cell, columnIndex) => cell === BRAILLE_BLANK ? [] : rowIndex * row.length + columnIndex
5521
- )
5522
- );
5523
- var fillStages = [
5524
- ...Array.from({ length: boltFillOrder.length + 1 }, (_, index) => index),
5525
- boltFillOrder.length,
5526
- 0
5527
- ];
5528
- var boltFillRanks = new Map(
5529
- boltFillOrder.map((cellIndex, fillIndex) => [cellIndex, fillIndex])
5530
- );
5531
- var STATUS_ROW_INDEX = Math.floor(boltRows.length / 2);
5532
- function formatPhase({ label, detail }) {
5533
- return detail ? `${chalk6__default.default.bold(label)} ${chalk6__default.default.dim(detail)}` : chalk6__default.default.bold(label);
5534
- }
5535
- function formatLoaderFrame({
5536
- phase,
5537
- fillStage
5538
- }) {
5539
- return boltRows.map((row, rowIndex) => {
5540
- const bolt = [...row].map((cell, columnIndex) => {
5541
- if (cell === BRAILLE_BLANK) return cell;
5542
- const cellIndex = rowIndex * row.length + columnIndex;
5543
- const fillRank = boltFillRanks.get(cellIndex);
5544
- const isFilled = fillRank !== void 0 && fillRank < fillStage;
5545
- return isFilled ? chalk6__default.default.bold.yellow(cell) : chalk6__default.default.dim.yellow(cell);
5546
- }).join("");
5547
- const status = rowIndex === STATUS_ROW_INDEX ? ` ${formatPhase(phase)}` : "";
5548
- return `${bolt}${status}`;
5549
- }).join("\n");
5550
- }
5551
- async function runWithSetupLoader({
5552
- promise,
5553
- ...phase
5554
- }) {
5555
- const startedAt = Date.now();
5556
- let fillFrameIndex = 0;
5557
- const loader = ora__default.default({
5558
- isEnabled: process.stderr.isTTY === true,
5559
- spinner: { interval: 80, frames: [""] },
5560
- text: formatLoaderFrame({
5561
- phase,
5562
- fillStage: fillStages[fillFrameIndex]
5563
- })
5564
- }).start();
5565
- const animationTimer = setInterval(() => {
5566
- fillFrameIndex = (fillFrameIndex + 1) % fillStages.length;
5567
- loader.text = formatLoaderFrame({
5568
- phase,
5569
- fillStage: fillStages[fillFrameIndex]
5570
- });
5571
- }, LIGHTNING_INTERVAL_MILLISECONDS);
5572
- animationTimer.unref?.();
5573
- try {
5574
- const result = await promise;
5575
- clearInterval(animationTimer);
5576
- const elapsedSeconds = ((Date.now() - startedAt) / 1e3).toFixed(1);
5577
- loader.succeed(`${formatPhase(phase)} ${chalk6__default.default.dim(`${elapsedSeconds}s`)}`);
5578
- return result;
5579
- } catch (error) {
5580
- clearInterval(animationTimer);
5581
- const elapsedSeconds = ((Date.now() - startedAt) / 1e3).toFixed(1);
5582
- loader.fail(`${formatPhase(phase)} ${chalk6__default.default.dim(`${elapsedSeconds}s`)}`);
5583
- throw error;
5584
- }
5585
- }
5586
5529
  function isRecord(value) {
5587
5530
  return typeof value === "object" && value !== null && !Array.isArray(value);
5588
5531
  }
@@ -5868,7 +5811,7 @@ async function offerPackageUpdate({
5868
5811
  }) {
5869
5812
  const { currentVersion, dependency } = packageState;
5870
5813
  if (!currentVersion || !dependency.checkForUpdates) return;
5871
- const update = await runWithSetupLoader({
5814
+ const update = await context.load({
5872
5815
  promise: context.checkForUpdates({
5873
5816
  packageName: dependency.name,
5874
5817
  currentVersion
@@ -5893,28 +5836,108 @@ async function offerPackageUpdate({
5893
5836
  })
5894
5837
  );
5895
5838
  }
5896
- async function prepareDependencies(context) {
5839
+ function getPackageStates(context) {
5897
5840
  const declaredPackageNames = getDeclaredPackageNames(context.cwd);
5898
5841
  const requiredPackages = REQUIRED_PACKAGES.filter(
5899
5842
  ({ typescriptOnly }) => !typescriptOnly || context.projectLanguage === "typescript"
5900
5843
  );
5901
- const packages = requiredPackages.map((dependency) => ({
5844
+ return requiredPackages.map((dependency) => ({
5902
5845
  dependency,
5846
+ declared: declaredPackageNames.has(dependency.name),
5903
5847
  currentVersion: declaredPackageNames.has(dependency.name) ? getInstalledPackageVersion({
5904
5848
  cwd: context.cwd,
5905
5849
  packageName: dependency.name
5906
5850
  }) : void 0
5907
5851
  }));
5852
+ }
5853
+ async function prepareDependencies(context) {
5854
+ const packages = getPackageStates(context);
5908
5855
  await offerMissingPackageInstall({
5909
5856
  context,
5910
- packages: packages.filter(
5911
- ({ dependency }) => !declaredPackageNames.has(dependency.name)
5912
- )
5857
+ packages: packages.filter(({ declared }) => !declared)
5913
5858
  });
5914
5859
  for (const packageState of packages) {
5915
5860
  await offerPackageUpdate({ context, packageState });
5916
5861
  }
5917
5862
  }
5863
+ var BRAILLE_BLANK = "\u2800";
5864
+ var LIGHTNING_INTERVAL_MILLISECONDS = 140;
5865
+ var boltRows = [
5866
+ "\u2800\u2800\u2880\u28FE\u2800\u2800\u2800\u2800",
5867
+ "\u2800\u2800\u28E0\u28FF\u28FF\u2800\u2800\u2800",
5868
+ "\u2800\u28FC\u28FF\u28FF\u28FF\u28C0\u28C0\u2840",
5869
+ "\u2808\u2809\u2809\u28FF\u28FF\u28FF\u285F\u2800",
5870
+ "\u2800\u2800\u2800\u28FF\u28FF\u280B\u2800\u2800",
5871
+ "\u2800\u2800\u2800\u287F\u2801\u2800\u2800\u2800"
5872
+ ];
5873
+ var boltFillOrder = boltRows.flatMap(
5874
+ (row, rowIndex) => [...row].flatMap(
5875
+ (cell, columnIndex) => cell === BRAILLE_BLANK ? [] : rowIndex * row.length + columnIndex
5876
+ )
5877
+ );
5878
+ var fillStages = [
5879
+ ...Array.from({ length: boltFillOrder.length + 1 }, (_, index) => index),
5880
+ boltFillOrder.length,
5881
+ 0
5882
+ ];
5883
+ var boltFillRanks = new Map(
5884
+ boltFillOrder.map((cellIndex, fillIndex) => [cellIndex, fillIndex])
5885
+ );
5886
+ var STATUS_ROW_INDEX = Math.floor(boltRows.length / 2);
5887
+ function formatPhase({ label, detail }) {
5888
+ return detail ? `${chalk6__default.default.bold(label)} ${chalk6__default.default.dim(detail)}` : chalk6__default.default.bold(label);
5889
+ }
5890
+ function formatLoaderFrame({
5891
+ phase,
5892
+ fillStage
5893
+ }) {
5894
+ return boltRows.map((row, rowIndex) => {
5895
+ const bolt = [...row].map((cell, columnIndex) => {
5896
+ if (cell === BRAILLE_BLANK) return cell;
5897
+ const cellIndex = rowIndex * row.length + columnIndex;
5898
+ const fillRank = boltFillRanks.get(cellIndex);
5899
+ const isFilled = fillRank !== void 0 && fillRank < fillStage;
5900
+ return isFilled ? chalk6__default.default.bold.yellow(cell) : chalk6__default.default.dim.yellow(cell);
5901
+ }).join("");
5902
+ const status = rowIndex === STATUS_ROW_INDEX ? ` ${formatPhase(phase)}` : "";
5903
+ return `${bolt}${status}`;
5904
+ }).join("\n");
5905
+ }
5906
+ async function runWithSetupLoader({
5907
+ promise,
5908
+ ...phase
5909
+ }) {
5910
+ const startedAt = Date.now();
5911
+ let fillFrameIndex = 0;
5912
+ const loader = ora__default.default({
5913
+ isEnabled: process.stderr.isTTY === true,
5914
+ spinner: { interval: 80, frames: [""] },
5915
+ text: formatLoaderFrame({
5916
+ phase,
5917
+ fillStage: fillStages[fillFrameIndex]
5918
+ })
5919
+ }).start();
5920
+ const animationTimer = setInterval(() => {
5921
+ fillFrameIndex = (fillFrameIndex + 1) % fillStages.length;
5922
+ loader.text = formatLoaderFrame({
5923
+ phase,
5924
+ fillStage: fillStages[fillFrameIndex]
5925
+ });
5926
+ }, LIGHTNING_INTERVAL_MILLISECONDS);
5927
+ animationTimer.unref?.();
5928
+ try {
5929
+ const result = await promise;
5930
+ clearInterval(animationTimer);
5931
+ const elapsedSeconds = ((Date.now() - startedAt) / 1e3).toFixed(1);
5932
+ loader.succeed(`${formatPhase(phase)} ${chalk6__default.default.dim(`${elapsedSeconds}s`)}`);
5933
+ return result;
5934
+ } catch (error) {
5935
+ clearInterval(animationTimer);
5936
+ const elapsedSeconds = ((Date.now() - startedAt) / 1e3).toFixed(1);
5937
+ loader.fail(`${formatPhase(phase)} ${chalk6__default.default.dim(`${elapsedSeconds}s`)}`);
5938
+ throw error;
5939
+ }
5940
+ }
5918
5941
 
5919
5942
  // src/plugins/setup/package-commands.ts
5920
5943
  function createLocalPackageCommand({
@@ -5953,7 +5976,10 @@ function formatPackageCommand({
5953
5976
  command,
5954
5977
  args
5955
5978
  }) {
5956
- return [command, ...args].join(" ");
5979
+ return [command, ...args].map(quoteShellArgument).join(" ");
5980
+ }
5981
+ function quoteShellArgument(value) {
5982
+ return /^[a-zA-Z0-9_@%+=:,./-]+$/.test(value) ? value : `'${value.replace(/'/g, `'"'"'`)}'`;
5957
5983
  }
5958
5984
 
5959
5985
  // src/plugins/setup/project.ts
@@ -6058,13 +6084,14 @@ async function runCommand({
6058
6084
  args = [],
6059
6085
  cwd,
6060
6086
  capture = true,
6061
- input
6087
+ input,
6088
+ load = runWithSetupLoader
6062
6089
  }) {
6063
6090
  const commandLabel = `$ ${command} ${args.join(" ")}`.trim();
6064
6091
  const execute = capture ? executeCapturedCommand({ command, args, cwd, input }) : executeStreamingCommand({ command, args, cwd, input });
6065
6092
  try {
6066
6093
  if (capture) {
6067
- return await runWithSetupLoader({
6094
+ return await load({
6068
6095
  promise: execute,
6069
6096
  label: "Running command",
6070
6097
  detail: commandLabel
@@ -6148,7 +6175,7 @@ function detectProjectLanguage({
6148
6175
  );
6149
6176
  return hasTypeScript ? "typescript" : "javascript";
6150
6177
  }
6151
- async function prepareProject(context) {
6178
+ async function initializeProject(context) {
6152
6179
  context.packageManager = detectPackageManager(context.cwd).name;
6153
6180
  console.log(`Using ${context.packageManager} for dependency installs.`);
6154
6181
  const hasExistingProject = fs.existsSync(path.join(context.cwd, "package.json"));
@@ -6164,182 +6191,161 @@ async function prepareProject(context) {
6164
6191
  cwd: context.cwd
6165
6192
  });
6166
6193
  }
6194
+ }
6195
+ async function prepareProject(context) {
6196
+ await initializeProject(context);
6167
6197
  await prepareDependencies(context);
6168
6198
  }
6169
6199
 
6170
6200
  // src/plugins/setup/context.ts
6171
6201
  function createWizardContext({
6172
- imports
6202
+ imports,
6203
+ agent = false
6173
6204
  }) {
6174
- return {
6205
+ const context = {
6175
6206
  cwd: process.cwd(),
6207
+ load: agent ? ({ promise }) => promise : runWithSetupLoader,
6176
6208
  imports,
6177
6209
  createAnswer: () => createCliAnswer({ mode: "closed-list" }),
6178
6210
  setupController: createSetupController(),
6179
- selectStoredLoginCredentials: () => chooseStoredLoginCredentials({ imports, options: {} }),
6180
- authenticateNewAccount: ({ entryPoint, headless }) => runAccountAuth({
6211
+ selectStoredLoginCredentials: () => chooseStoredLoginCredentials({
6212
+ imports,
6213
+ options: {},
6214
+ ...agent ? {
6215
+ prompt: async (options) => {
6216
+ const credentialChoices = buildCredentialsChoices(options);
6217
+ const choices = credentialChoices.map(({ name, value }) => ({
6218
+ label: name,
6219
+ value: value.action === "create" ? "create" : `account:${value.credentials.name}`,
6220
+ result: value
6221
+ }));
6222
+ const selected = await resolveSetupSelect({
6223
+ answer: context.createAnswer(),
6224
+ controller: context.setupController,
6225
+ message: CREDENTIALS_PICKER_QUESTION,
6226
+ choices
6227
+ });
6228
+ const selectedIndex = choices.findIndex(
6229
+ ({ value }) => value === selected
6230
+ );
6231
+ return choices[selectedIndex].result;
6232
+ }
6233
+ } : {}
6234
+ }),
6235
+ authenticateNewAccount: ({ entryPoint, ...options }) => runAccountAuth({
6181
6236
  imports,
6182
- options: { headless },
6237
+ options,
6183
6238
  entryPoint,
6184
6239
  storedCredentialsMode: "fresh",
6185
- preserveExistingCredentials: true
6240
+ preserveExistingCredentials: true,
6241
+ useBrowserCallback: agent && options.headless === false,
6242
+ silent: agent && options.headless
6186
6243
  }),
6187
6244
  checkForUpdates,
6188
6245
  openUrl: async (url) => {
6189
6246
  await open__default.default(url);
6190
6247
  },
6191
- runCommand,
6248
+ runCommand: agent ? (options) => runCommand({
6249
+ ...options,
6250
+ capture: true,
6251
+ load: ({ promise, detail }) => {
6252
+ console.log(detail);
6253
+ return promise;
6254
+ }
6255
+ }) : runCommand,
6192
6256
  packageManager: detectPackageManager(process.cwd()).name,
6193
6257
  projectLanguage: "typescript"
6194
6258
  };
6259
+ return context;
6260
+ }
6261
+ var SetupStepSchema = zod.z.enum([
6262
+ "directory",
6263
+ "project",
6264
+ "install",
6265
+ "sdk-update",
6266
+ "cli-update",
6267
+ "skill",
6268
+ "account",
6269
+ "account-flow",
6270
+ "account-environment",
6271
+ "authorize",
6272
+ "confirm-account",
6273
+ "connections",
6274
+ "slack",
6275
+ "slack-overwrite",
6276
+ "slack-existing",
6277
+ "slack-retry",
6278
+ "handoff"
6279
+ ]);
6280
+ var SetupSchema = zod.z.object({
6281
+ agent: zod.z.boolean().optional().describe(
6282
+ "For AI agents and callers without a terminal: print the next setup question and the exact command to run for each answer."
6283
+ ),
6284
+ step: SetupStepSchema.optional().describe(
6285
+ "Setup operation to inspect or execute."
6286
+ ),
6287
+ nonInteractive: zod.z.boolean().optional().describe("Execute the named setup step without terminal prompts."),
6288
+ answer: zod.z.string().optional().describe(
6289
+ "User selection or input for a non-interactive setup step. Omitted confirmations use the terminal wizard's default."
6290
+ ),
6291
+ flow: zod.z.enum(["login", "signup"]).optional().describe(
6292
+ "Account flow selected for the environment and OAuth callback steps."
6293
+ ),
6294
+ language: zod.z.enum(["javascript", "typescript"]).optional().describe(
6295
+ "Project language for dependency installation and the Slack demo."
6296
+ )
6297
+ }).describe(
6298
+ "Set up the Zapier SDK in an existing or new project. Runs an interactive terminal wizard; AI agents should use --agent instead."
6299
+ );
6300
+
6301
+ // package.json
6302
+ var package_default = {
6303
+ version: "0.87.2"};
6304
+ var agentLabels = {
6305
+ claude: "Claude Code",
6306
+ codex: "Codex",
6307
+ cursor: "Cursor",
6308
+ "vscode-copilot": "VS Code Copilot"
6309
+ };
6310
+ function buildAgentPrompt({
6311
+ context
6312
+ }) {
6313
+ const listConnectionsCommand = formatPackageCommand(
6314
+ createLocalPackageCommand({
6315
+ packageManager: context.packageManager,
6316
+ binary: "zapier-sdk",
6317
+ args: ["list-connections", "--owner", "me", "--json"]
6318
+ })
6319
+ );
6320
+ const listActionsCommand = formatPackageCommand(
6321
+ createLocalPackageCommand({
6322
+ packageManager: context.packageManager,
6323
+ binary: "zapier-sdk",
6324
+ args: ["list-actions", "APP_KEY"]
6325
+ })
6326
+ );
6327
+ return `Start by running this command to discover my active connected apps outside of your sandbox:
6328
+
6329
+ ${listConnectionsCommand}
6330
+
6331
+ Using the connected apps returned by that command:
6332
+
6333
+ - Pick 1-3 apps and show commands I can use to explore their actions, such as: ${listActionsCommand}
6334
+ - Do not run those list-actions commands.
6335
+ - Suggest 3 specific ideas based on my connected apps. Each idea should read from one app, optionally process data, and write to another.
6336
+ - Do not suggest event-driven workflows.
6337
+ - Keep each idea to one sentence. Example: "pull in-progress Jira issues and DM yourself a summary on Slack".
6338
+
6339
+ 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.`;
6195
6340
  }
6196
- var SetupSchema = zod.z.object({}).describe("Set up the Zapier SDK in an existing or new project");
6197
- function printAuthCommand({
6341
+ function printAgentPrompt({
6198
6342
  context,
6199
- flow,
6200
- headless
6343
+ message
6201
6344
  }) {
6202
- const command = createLocalPackageCommand({
6203
- packageManager: context.packageManager,
6204
- binary: "zapier-sdk",
6205
- args: [flow, ...headless ? ["--headless"] : []]
6206
- });
6207
- console.log(`$ ${formatPackageCommand(command)}`);
6208
- if (headless) {
6209
- console.log(
6210
- "Open the printed URL in another browser, then paste the final OAuth callback URL when prompted."
6211
- );
6212
- }
6213
- }
6214
- function isCredentialWriteError(error) {
6215
- const code = typeof error === "object" && error !== null && "code" in error && typeof error.code === "string" ? error.code : "";
6216
- if (/^(eacces|eperm)$/i.test(code)) return true;
6217
- const message = error instanceof Error ? error.message : String(error);
6218
- return /permission|sandbox|eacces|eperm/i.test(message);
6219
- }
6220
- async function confirmAuthentication(context) {
6221
- const { data: profile } = await runWithSetupLoader({
6222
- promise: context.imports.getProfile({}),
6223
- label: "Confirming Zapier account"
6224
- });
6225
- context.accountEmail = profile.email;
6226
- console.log(`Authenticated as ${profile.email}.`);
6227
- }
6228
- async function authenticate(context) {
6229
- const activeProfile = await runWithSetupLoader({
6230
- promise: context.imports.getProfile({}).then(({ data }) => data).catch((error) => {
6231
- if (zapierSdk.isZapierAuthenticationError(error)) return void 0;
6232
- throw error;
6233
- }),
6234
- label: "Checking Zapier authentication"
6235
- });
6236
- const storedCredentialsSelection = await context.selectStoredLoginCredentials();
6237
- if (storedCredentialsSelection === "switch") {
6238
- await confirmAuthentication(context);
6239
- return;
6240
- }
6241
- if (storedCredentialsSelection === "none" && activeProfile) {
6242
- const shouldLogout = await resolveSetupConfirm({
6243
- answer: context.createAnswer(),
6244
- controller: context.setupController,
6245
- message: `You are already logged in as "${activeProfile.email}".
6246
- Logging out will delete these credentials and may interrupt other Zapier SDK or CLI sessions using them.
6247
- Log out and use a different account?`,
6248
- defaultValue: false
6249
- });
6250
- if (!shouldLogout) {
6251
- context.accountEmail = activeProfile.email;
6252
- console.log(`Continuing as ${activeProfile.email}.`);
6253
- return;
6254
- }
6255
- const logoutCommand = createLocalPackageCommand({
6256
- packageManager: context.packageManager,
6257
- binary: "zapier-sdk",
6258
- args: ["logout"]
6259
- });
6260
- console.log(`$ ${formatPackageCommand(logoutCommand)}`);
6261
- await context.imports.logout({});
6262
- }
6263
- const flow = await resolveSetupSelect({
6264
- answer: context.createAnswer(),
6265
- controller: context.setupController,
6266
- message: "Zapier account:",
6267
- choices: [
6268
- { label: "Log in to an existing account", value: "login" },
6269
- { label: "Create a new Zapier account", value: "signup" }
6270
- ]
6271
- });
6272
- const environment = await resolveSetupSelect({
6273
- answer: context.createAnswer(),
6274
- controller: context.setupController,
6275
- message: "Environment:",
6276
- choices: [
6277
- { label: "Local machine with a browser", value: "local" },
6278
- { label: "Remote or headless", value: "headless" }
6279
- ]
6280
- });
6281
- const headless = environment === "headless";
6282
- printAuthCommand({ context, flow, headless });
6283
- try {
6284
- if (storedCredentialsSelection === "create") {
6285
- await context.authenticateNewAccount({ entryPoint: flow, headless });
6286
- } else {
6287
- const runAuth = flow === "login" ? context.imports.login : context.imports.signup;
6288
- await runAuth({ headless });
6289
- }
6290
- } catch (error) {
6291
- if (isCredentialWriteError(error)) {
6292
- console.error(
6293
- "Authentication could not write credentials. A permissions or sandbox restriction may be blocking disk access."
6294
- );
6295
- }
6296
- throw error;
6297
- }
6298
- await confirmAuthentication(context);
6299
- }
6300
- var agentLabels = {
6301
- claude: "Claude Code",
6302
- codex: "Codex",
6303
- cursor: "Cursor",
6304
- "vscode-copilot": "VS Code Copilot"
6305
- };
6306
- function buildAgentPrompt({ context }) {
6307
- const listConnectionsCommand = formatPackageCommand(
6308
- createLocalPackageCommand({
6309
- packageManager: context.packageManager,
6310
- binary: "zapier-sdk",
6311
- args: ["list-connections", "--owner", "me", "--json"]
6312
- })
6313
- );
6314
- const listActionsCommand = formatPackageCommand(
6315
- createLocalPackageCommand({
6316
- packageManager: context.packageManager,
6317
- binary: "zapier-sdk",
6318
- args: ["list-actions", "APP_KEY"]
6319
- })
6320
- );
6321
- return `Start by running this command to discover my active connected apps outside of your sandbox:
6322
-
6323
- ${listConnectionsCommand}
6324
-
6325
- Using the connected apps returned by that command:
6326
-
6327
- - Pick 1-3 apps and show commands I can use to explore their actions, such as: ${listActionsCommand}
6328
- - Do not run those list-actions commands.
6329
- - Suggest 3 specific ideas based on my connected apps. Each idea should read from one app, optionally process data, and write to another.
6330
- - Do not suggest event-driven workflows.
6331
- - Keep each idea to one sentence. Example: "pull in-progress Jira issues and DM yourself a summary on Slack".
6332
-
6333
- 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.`;
6334
- }
6335
- function printAgentPrompt({
6336
- context,
6337
- message
6338
- }) {
6339
- console.log();
6340
- console.log(chalk6__default.default.bgYellow.black.bold(message));
6341
- console.log();
6342
- console.log(buildAgentPrompt({ context }));
6345
+ console.log();
6346
+ console.log(chalk6__default.default.bgYellow.black.bold(message));
6347
+ console.log();
6348
+ console.log(buildAgentPrompt({ context }));
6343
6349
  }
6344
6350
  async function openAgentHandoff(context) {
6345
6351
  const agent = await resolveSetupSelect({
@@ -6410,27 +6416,291 @@ async function openAgentHandoff(context) {
6410
6416
  message: "If it didn't load correctly, paste this prompt into your agent:"
6411
6417
  });
6412
6418
  }
6419
+
6420
+ // src/plugins/setup/agent-output.ts
6421
+ var REQUIRED_STEPS = [
6422
+ "directory",
6423
+ "install"
6424
+ ];
6425
+ function formatSetupCommand({
6426
+ context,
6427
+ target,
6428
+ answer
6429
+ }) {
6430
+ const args = [
6431
+ "setup",
6432
+ "--step",
6433
+ target.step,
6434
+ "--non-interactive",
6435
+ ...target.flow ? ["--flow", target.flow] : [],
6436
+ "--language",
6437
+ context.projectLanguage,
6438
+ ...answer !== void 0 ? ["--answer", answer] : []
6439
+ ];
6440
+ return formatPackageCommand(
6441
+ SOURCE_CLI_PATH ? {
6442
+ command: process.execPath,
6443
+ args: [
6444
+ "--import",
6445
+ module$1.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index.cjs', document.baseURI).href))).resolve("tsx"),
6446
+ "--conditions",
6447
+ "source",
6448
+ SOURCE_CLI_PATH,
6449
+ ...args
6450
+ ]
6451
+ } : createPackageRunnerCommand({
6452
+ packageManager: context.packageManager,
6453
+ packageName: `@zapier/zapier-sdk-cli@${package_default.version}`,
6454
+ args
6455
+ })
6456
+ );
6457
+ }
6458
+ function printAgentStep({
6459
+ context,
6460
+ target,
6461
+ question
6462
+ }) {
6463
+ console.log(
6464
+ `Step: ${target.step}
6465
+ Working directory: ${context.cwd}
6466
+ Detected package manager: ${context.packageManager}
6467
+ Project language: ${context.projectLanguage}`
6468
+ );
6469
+ if (!question) {
6470
+ console.log(
6471
+ `Run this exact command:
6472
+ ${formatSetupCommand({ context, target })}`
6473
+ );
6474
+ } else {
6475
+ if (target.step === "authorize" && context.authorizationUrl)
6476
+ console.log(
6477
+ `Give the user this URL to open in a browser where they can sign in to Zapier:
6478
+ ${context.authorizationUrl}
6479
+ 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.`
6480
+ );
6481
+ console.log(`Ask the user this question verbatim: "${question.message}"`);
6482
+ if (question.type === "select") {
6483
+ for (const { value, label } of question.choices)
6484
+ console.log(`${JSON.stringify(value)}: ${label}`);
6485
+ for (const { value } of question.choices) {
6486
+ const decision = value === "true" ? "agrees" : value === "false" ? "declines" : `chooses ${JSON.stringify(value)}`;
6487
+ if (value === "false" && REQUIRED_STEPS.includes(target.step))
6488
+ console.log("If the user declines, stop setup.");
6489
+ else
6490
+ console.log(
6491
+ `If the user ${decision}, run this exact command:
6492
+ ${formatSetupCommand({ context, target, answer: value })}`
6493
+ );
6494
+ }
6495
+ } else {
6496
+ console.log(
6497
+ `Run this exact command with their answer as one shell-quoted argument (an empty answer is ''):
6498
+ ${formatSetupCommand({ context, target, answer: "<answer>" })}`
6499
+ );
6500
+ }
6501
+ if (target.step === "account-environment")
6502
+ console.log(
6503
+ `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.`
6504
+ );
6505
+ console.log(
6506
+ "Use your tool for asking the user questions if you have one. Do not answer on the user's behalf."
6507
+ );
6508
+ }
6509
+ console.log(
6510
+ "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."
6511
+ );
6512
+ }
6513
+ function printAgentCompletion({
6514
+ context,
6515
+ cancelled = false
6516
+ }) {
6517
+ console.log(
6518
+ cancelled ? "Setup cancelled." : `Setup walkthrough finished. Report which operations succeeded, failed, or were skipped based on their command results.
6519
+ Learn more: https://docs.zapier.com/sdk
6520
+
6521
+ Continue in this agent. Do not open another agent or ask the user to choose one.
6522
+ ${buildAgentPrompt({ context })}`
6523
+ );
6524
+ }
6525
+
6526
+ // src/plugins/setup/authentication.ts
6527
+ function printAuthCommand({
6528
+ context,
6529
+ flow,
6530
+ headless = false
6531
+ }) {
6532
+ const command = createLocalPackageCommand({
6533
+ packageManager: context.packageManager,
6534
+ binary: "zapier-sdk",
6535
+ args: [flow, ...headless ? ["--headless"] : []]
6536
+ });
6537
+ console.log(`$ ${formatPackageCommand(command)}`);
6538
+ if (headless) {
6539
+ console.log(
6540
+ "Open the printed URL in another browser, then paste the final OAuth callback URL when prompted."
6541
+ );
6542
+ }
6543
+ }
6544
+ function isCredentialWriteError(error) {
6545
+ const code = typeof error === "object" && error !== null && "code" in error && typeof error.code === "string" ? error.code : "";
6546
+ if (/^(eacces|eperm)$/i.test(code)) return true;
6547
+ const message = error instanceof Error ? error.message : String(error);
6548
+ return /permission|sandbox|eacces|eperm/i.test(message);
6549
+ }
6550
+ async function confirmAuthentication(context) {
6551
+ const { data: profile } = await context.load({
6552
+ promise: context.imports.getProfile({}),
6553
+ label: "Confirming Zapier account"
6554
+ });
6555
+ context.accountEmail = profile.email;
6556
+ console.log(`Authenticated as ${profile.email}.`);
6557
+ }
6558
+ function selectAccountFlow(context) {
6559
+ return resolveSetupSelect({
6560
+ answer: context.createAnswer(),
6561
+ controller: context.setupController,
6562
+ message: "Zapier account:",
6563
+ choices: [
6564
+ { label: "Log in to an existing account", value: "login" },
6565
+ { label: "Create a new Zapier account", value: "signup" }
6566
+ ]
6567
+ });
6568
+ }
6569
+ function selectAuthenticationEnvironment(context) {
6570
+ return resolveSetupSelect({
6571
+ answer: context.createAnswer(),
6572
+ controller: context.setupController,
6573
+ message: "Environment:",
6574
+ choices: [
6575
+ { label: "Local machine with a browser", value: "local" },
6576
+ { label: "Remote or headless", value: "headless" }
6577
+ ]
6578
+ });
6579
+ }
6580
+ async function selectAccount(context) {
6581
+ const activeProfile = await context.load({
6582
+ promise: context.imports.getProfile({}).then(({ data }) => data).catch((error) => {
6583
+ if (zapierSdk.isZapierAuthenticationError(error)) return void 0;
6584
+ throw error;
6585
+ }),
6586
+ label: "Checking Zapier authentication"
6587
+ });
6588
+ const storedCredentialsSelection = await context.selectStoredLoginCredentials();
6589
+ if (storedCredentialsSelection === "none" && activeProfile) {
6590
+ const shouldLogout = await resolveSetupConfirm({
6591
+ answer: context.createAnswer(),
6592
+ controller: context.setupController,
6593
+ message: `You are already logged in as "${activeProfile.email}".
6594
+ Logging out will delete these credentials and may interrupt other Zapier SDK or CLI sessions using them.
6595
+ Log out and use a different account?`,
6596
+ defaultValue: false
6597
+ });
6598
+ if (!shouldLogout) {
6599
+ context.accountEmail = activeProfile.email;
6600
+ console.log(`Continuing as ${activeProfile.email}.`);
6601
+ return "continue";
6602
+ }
6603
+ printAuthCommand({ context, flow: "logout" });
6604
+ await context.imports.logout({});
6605
+ }
6606
+ return storedCredentialsSelection;
6607
+ }
6608
+ async function authenticate(context) {
6609
+ const next = await runAuthenticationStep({ context, options: {} });
6610
+ if (next.step === "confirm-account") await confirmAuthentication(context);
6611
+ }
6612
+ async function runAuthenticationStep({
6613
+ context,
6614
+ options
6615
+ }) {
6616
+ const { step } = options;
6617
+ let newAccount = step === "account-environment";
6618
+ if (!step || step === "account") {
6619
+ const selection = await selectAccount(context);
6620
+ if (selection === "switch") return { step: "confirm-account" };
6621
+ if (selection === "continue")
6622
+ return { step: step ? "confirm-account" : "connections" };
6623
+ if (step) return { step: "account-flow" };
6624
+ newAccount = selection === "create";
6625
+ }
6626
+ if (step === "confirm-account") {
6627
+ await confirmAuthentication(context);
6628
+ return { step: "connections" };
6629
+ }
6630
+ const flow = !step || step === "account-flow" ? await selectAccountFlow(context) : options.flow;
6631
+ if (step === "account-flow")
6632
+ return {
6633
+ step: "account-environment",
6634
+ flow
6635
+ };
6636
+ if (!flow)
6637
+ throw new ZapierCliValidationError(
6638
+ "This setup step requires --flow login or --flow signup."
6639
+ );
6640
+ let headless = true;
6641
+ let callbackUrl;
6642
+ let recoveryMessage;
6643
+ if (step === "authorize") {
6644
+ recoveryMessage = `Generate a fresh authorization URL with \`${formatSetupCommand(
6645
+ {
6646
+ context,
6647
+ target: { step: "account-environment", flow },
6648
+ answer: "headless"
6649
+ }
6650
+ )}\`.`;
6651
+ const pending = getPendingOauthFlow({ entryPoint: flow, recoveryMessage });
6652
+ context.authorizationUrl = pending.transaction.browserAuthUrl;
6653
+ callbackUrl = await resolveSetupInput({
6654
+ answer: context.createAnswer(),
6655
+ controller: context.setupController,
6656
+ message: OAUTH_CALLBACK_QUESTION
6657
+ });
6658
+ } else {
6659
+ headless = await selectAuthenticationEnvironment(context) === "headless";
6660
+ if (!step) printAuthCommand({ context, flow, headless });
6661
+ }
6662
+ const authOptions = {
6663
+ headless,
6664
+ ...step ? { nonInteractive: true } : {},
6665
+ ...callbackUrl !== void 0 ? { callbackUrl } : {}
6666
+ };
6667
+ try {
6668
+ if (newAccount) {
6669
+ await context.authenticateNewAccount({
6670
+ entryPoint: flow,
6671
+ ...authOptions
6672
+ });
6673
+ } else {
6674
+ await context.imports[flow](authOptions);
6675
+ }
6676
+ } catch (error) {
6677
+ if (recoveryMessage) console.error(recoveryMessage);
6678
+ if (isCredentialWriteError(error)) {
6679
+ console.error(
6680
+ "Authentication could not write credentials. A permissions or sandbox restriction may be blocking disk access."
6681
+ );
6682
+ }
6683
+ throw error;
6684
+ }
6685
+ return step === "account-environment" && headless ? { step: "authorize", flow } : { step: "confirm-account" };
6686
+ }
6413
6687
  var CONNECTIONS_URL = "https://zapier.com/app/assets/connections";
6414
6688
  var CONNECTION_DISPLAY_LIMIT = 10;
6415
- async function ensureConnectionsAvailable(context) {
6416
- const page = await runWithSetupLoader({
6689
+ async function showActiveConnections(context) {
6690
+ const page = await context.load({
6417
6691
  promise: context.imports.listConnections({
6418
6692
  owner: "me",
6419
6693
  maxItems: CONNECTION_DISPLAY_LIMIT
6420
6694
  }),
6421
6695
  label: "Loading app connections"
6422
6696
  });
6423
- if (page.data.length === 0) {
6424
- console.log(
6425
- `No active connections found. Connect or reconnect an app at ${CONNECTIONS_URL}, then come back and run setup again.`
6426
- );
6427
- throw new ZapierCliExitError(
6428
- "Setup stopped before completion because no active connections are available."
6429
- );
6430
- }
6697
+ if (page.data.length === 0) return false;
6431
6698
  const table = new Table__default.default({
6432
6699
  head: ["App", "Connection"],
6433
- style: { compact: true }
6700
+ style: {
6701
+ compact: true,
6702
+ ...process.stdout.isTTY ? {} : { head: [], border: [] }
6703
+ }
6434
6704
  });
6435
6705
  table.push(
6436
6706
  ...page.data.map((connection) => [
@@ -6442,11 +6712,22 @@ async function ensureConnectionsAvailable(context) {
6442
6712
  `Active Zapier connections (showing up to ${CONNECTION_DISPLAY_LIMIT}):`
6443
6713
  );
6444
6714
  console.log(table.toString());
6715
+ return true;
6716
+ }
6717
+ async function ensureConnectionsAvailable(context) {
6718
+ if (await showActiveConnections(context)) return;
6719
+ console.log(
6720
+ `No active connections found. Connect or reconnect an app at ${CONNECTIONS_URL}, then come back and run setup again.`
6721
+ );
6722
+ throw new ZapierCliExitError(
6723
+ "Setup stopped before completion because no active connections are available."
6724
+ );
6445
6725
  }
6446
6726
  var CONNECTION_PLACEHOLDER = "__ZAPIER_SDK_SLACK_CONNECTION__";
6447
6727
  var EMAIL_PLACEHOLDER = "__ZAPIER_SDK_ACCOUNT_EMAIL__";
6448
6728
  var APP_PLACEHOLDER = "__ZAPIER_SDK_SLACK_APP__";
6449
6729
  var SLACK_SLUG = "slack";
6730
+ var SLACK_RETRY_QUESTION = "Slack email for one retry after the failed test:";
6450
6731
  function getSlackTestPath(context) {
6451
6732
  const extension = context.projectLanguage === "typescript" ? "ts" : "mjs";
6452
6733
  return path.join("src", "sdk-demo", `zapier-slack-test.${extension}`);
@@ -6456,7 +6737,7 @@ function getSlackTestTemplatePath(context) {
6456
6737
  return path.join(TEMPLATES_DIR, "setup", `zapier-slack-test.${extension}`);
6457
6738
  }
6458
6739
  async function findSlackConnection(context) {
6459
- const page = await runWithSetupLoader({
6740
+ const page = await context.load({
6460
6741
  promise: context.imports.listConnections({
6461
6742
  owner: "me",
6462
6743
  app: SLACK_SLUG,
@@ -6477,17 +6758,22 @@ function renderSlackTest({
6477
6758
  }) {
6478
6759
  return fs.readFileSync(getSlackTestTemplatePath(context), "utf8").replace(APP_PLACEHOLDER, () => toTemplateString(app)).replace(CONNECTION_PLACEHOLDER, () => toTemplateString(connection)).replace(EMAIL_PLACEHOLDER, () => toTemplateString(email));
6479
6760
  }
6480
- function writeSlackTest({
6761
+ async function createAndRunSlackTest({
6481
6762
  context,
6482
- app,
6483
6763
  connection,
6484
- email
6764
+ email,
6765
+ overwrite = true
6485
6766
  }) {
6486
6767
  const slackTestPath = getSlackTestPath(context);
6487
6768
  const path2 = path.join(context.cwd, slackTestPath);
6488
6769
  if (!fs.existsSync(path.dirname(path2))) fs.mkdirSync(path.dirname(path2), { recursive: true });
6489
- fs.writeFileSync(path2, renderSlackTest({ context, app, connection, email }));
6770
+ fs.writeFileSync(
6771
+ path2,
6772
+ renderSlackTest({ context, app: SLACK_SLUG, connection, email }),
6773
+ { flag: overwrite ? "w" : "wx" }
6774
+ );
6490
6775
  console.log(`Created ${slackTestPath}.`);
6776
+ return trySlackTest(context);
6491
6777
  }
6492
6778
  async function executeSlackTest(context) {
6493
6779
  const slackTestPath = getSlackTestPath(context);
@@ -6515,75 +6801,76 @@ async function executeSlackTest(context) {
6515
6801
  async function trySlackTest(context) {
6516
6802
  try {
6517
6803
  await executeSlackTest(context);
6804
+ (context.outcomes ?? (context.outcomes = {})).slack = "succeeded";
6518
6805
  return true;
6519
6806
  } catch (error) {
6807
+ (context.outcomes ?? (context.outcomes = {})).slack = "failed";
6520
6808
  const message = error instanceof Error ? error.message : String(error);
6521
6809
  console.warn(`Slack demo failed: ${message}. Setup will continue.`);
6522
6810
  return false;
6523
6811
  }
6524
6812
  }
6525
- async function offerSlackTest(context) {
6813
+ async function runSlackStep({
6814
+ context,
6815
+ step,
6816
+ connection
6817
+ }) {
6526
6818
  if (!context.accountEmail) {
6527
6819
  console.log("No account email found; skipping the Slack demo.");
6528
- return;
6820
+ return "handoff";
6529
6821
  }
6530
- const slackConnection = await findSlackConnection(context);
6531
- if (!slackConnection) {
6822
+ if (!connection) {
6532
6823
  console.log("No active Slack connection found; skipping the Slack demo.");
6533
- return;
6824
+ return "handoff";
6534
6825
  }
6535
- const slackTestPath = getSlackTestPath(context);
6536
- const shouldRun = await resolveSetupConfirm({
6537
- answer: context.createAnswer(),
6538
- controller: context.setupController,
6539
- message: `Run the Slack self-DM test using ${context.accountEmail}?`,
6540
- defaultValue: true
6541
- });
6542
- if (!shouldRun) return;
6543
- const path2 = path.join(context.cwd, slackTestPath);
6544
- if (fs.existsSync(path2)) {
6545
- const shouldOverwrite = await resolveSetupConfirm({
6826
+ if (step === "slack-retry") {
6827
+ const email = await resolveSetupInput({
6546
6828
  answer: context.createAnswer(),
6547
6829
  controller: context.setupController,
6548
- message: `${slackTestPath} already exists. Is it okay to overwrite it?`,
6549
- defaultValue: false
6830
+ message: SLACK_RETRY_QUESTION
6550
6831
  });
6551
- if (!shouldOverwrite) {
6552
- console.log("Keeping the existing Slack test file.");
6553
- const shouldRunExisting = await resolveSetupConfirm({
6554
- answer: context.createAnswer(),
6555
- controller: context.setupController,
6556
- message: `Run the existing ${slackTestPath}?`,
6557
- defaultValue: false
6832
+ if (email)
6833
+ await createAndRunSlackTest({
6834
+ context,
6835
+ connection: connection.id,
6836
+ email
6558
6837
  });
6559
- if (shouldRunExisting) await trySlackTest(context);
6560
- return;
6561
- }
6838
+ else console.log("Skipping the Slack test.");
6839
+ return "handoff";
6562
6840
  }
6563
- let email = context.accountEmail;
6564
- writeSlackTest({
6565
- context,
6566
- app: SLACK_SLUG,
6567
- connection: slackConnection.id,
6568
- email
6569
- });
6570
- if (await trySlackTest(context)) return;
6571
- email = await resolveSetupInput({
6841
+ const slackTestPath = getSlackTestPath(context);
6842
+ const shouldRun = await resolveSetupConfirm({
6572
6843
  answer: context.createAnswer(),
6573
6844
  controller: context.setupController,
6574
- message: "Slack email for one retry after the failed test:"
6845
+ 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}?`,
6846
+ defaultValue: step === "slack"
6575
6847
  });
6576
- if (!email) {
6577
- console.log("Skipping the Slack test.");
6578
- return;
6848
+ if (!shouldRun) {
6849
+ if (step === "slack-overwrite") {
6850
+ console.log("Keeping the existing Slack test file.");
6851
+ return "slack-existing";
6852
+ }
6853
+ return "handoff";
6854
+ }
6855
+ if (step === "slack" && fs.existsSync(path.join(context.cwd, slackTestPath)))
6856
+ return "slack-overwrite";
6857
+ if (step === "slack-existing") {
6858
+ await trySlackTest(context);
6859
+ return "handoff";
6579
6860
  }
6580
- writeSlackTest({
6861
+ const succeeded = await createAndRunSlackTest({
6581
6862
  context,
6582
- app: SLACK_SLUG,
6583
- connection: slackConnection.id,
6584
- email
6863
+ connection: connection.id,
6864
+ email: context.accountEmail,
6865
+ overwrite: step === "slack-overwrite"
6585
6866
  });
6586
- await trySlackTest(context);
6867
+ return succeeded ? "handoff" : "slack-retry";
6868
+ }
6869
+ async function offerSlackTest(context) {
6870
+ const connection = context.accountEmail ? await findSlackConnection(context) : void 0;
6871
+ let step = "slack";
6872
+ while (step !== "handoff")
6873
+ step = await runSlackStep({ context, step, connection });
6587
6874
  }
6588
6875
 
6589
6876
  // src/plugins/setup/skill.ts
@@ -6609,7 +6896,9 @@ async function installOptionalSkill(context) {
6609
6896
  cwd: context.cwd,
6610
6897
  capture: true
6611
6898
  });
6899
+ (context.outcomes ?? (context.outcomes = {})).skill = "succeeded";
6612
6900
  } catch {
6901
+ (context.outcomes ?? (context.outcomes = {})).skill = "failed";
6613
6902
  console.warn(
6614
6903
  `Could not install the optional Zapier SDK skill. To install it manually, run: ${formatPackageCommand(command)}`
6615
6904
  );
@@ -6666,6 +6955,177 @@ async function runWizard(context) {
6666
6955
  console.log(chalk6__default.default.bgCyan.black.bold(" Next steps "));
6667
6956
  await openAgentHandoff(context);
6668
6957
  }
6958
+ var UNPROMPTED_STEPS = [
6959
+ "project",
6960
+ "confirm-account",
6961
+ "connections"
6962
+ ];
6963
+ var ZapierSetupQuestionSignal = class extends zapierSdk.ZapierSignal {
6964
+ constructor(question) {
6965
+ super(question.message);
6966
+ this.question = question;
6967
+ this.name = "ZapierSetupQuestionSignal";
6968
+ this.code = "ZAPIER_SETUP_QUESTION";
6969
+ }
6970
+ };
6971
+ function isZapierSetupQuestionSignal(error) {
6972
+ return zapierSdk.isZapierSignal(error) && error.code === "ZAPIER_SETUP_QUESTION";
6973
+ }
6974
+ function createSetupAnswer({
6975
+ answer,
6976
+ execute
6977
+ }) {
6978
+ let answered = false;
6979
+ return async ({ result: { question } }) => {
6980
+ if (question.type === "collection")
6981
+ throw new ZapierCliValidationError(
6982
+ "Setup expects a selection or text input."
6983
+ );
6984
+ if (!execute) throw new ZapierSetupQuestionSignal(question);
6985
+ if (answered)
6986
+ throw new ZapierCliValidationError(
6987
+ "Each setup step can answer only one question. Inspect the next question with --agent and run it in a separate command."
6988
+ );
6989
+ answered = true;
6990
+ const confirmation = question.type === "select" && question.choices.some(({ value }) => value === "true") && question.choices.some(({ value }) => value === "false");
6991
+ if (answer === void 0 && !confirmation)
6992
+ throw new ZapierCliValidationError(
6993
+ "This setup step requires --answer. Inspect it with --agent and pass the user's selection or input."
6994
+ );
6995
+ if (question.type === "input")
6996
+ return { type: "custom", value: answer?.trim() ?? "" };
6997
+ const selected = answer?.trim() ?? question.choices[0].value;
6998
+ if (!question.choices.some(({ value }) => value === selected))
6999
+ throw new ZapierCliValidationError(
7000
+ `Answer must be one of: ${question.choices.map(({ value }) => value).join(", ")}.`
7001
+ );
7002
+ return { type: "choose", value: selected };
7003
+ };
7004
+ }
7005
+ async function runSetupStep({
7006
+ context,
7007
+ target
7008
+ }) {
7009
+ switch (target.step) {
7010
+ case "directory":
7011
+ await chooseDirectory(context);
7012
+ return { step: "project" };
7013
+ case "project":
7014
+ await ensureSupportedNode(context);
7015
+ await initializeProject(context);
7016
+ return { step: "install" };
7017
+ case "install":
7018
+ await offerMissingPackageInstall({
7019
+ context,
7020
+ packages: getPackageStates(context).filter(({ declared }) => !declared)
7021
+ });
7022
+ return { step: "sdk-update" };
7023
+ case "sdk-update":
7024
+ case "cli-update": {
7025
+ const name = target.step === "sdk-update" ? "@zapier/zapier-sdk" : "@zapier/zapier-sdk-cli";
7026
+ const packageState = getPackageStates(context).find(
7027
+ ({ dependency }) => dependency.name === name
7028
+ );
7029
+ if (packageState) await offerPackageUpdate({ context, packageState });
7030
+ return { step: target.step === "sdk-update" ? "cli-update" : "skill" };
7031
+ }
7032
+ case "skill":
7033
+ await installOptionalSkill(context);
7034
+ return { step: "account" };
7035
+ case "account":
7036
+ case "account-flow":
7037
+ case "account-environment":
7038
+ case "authorize":
7039
+ case "confirm-account":
7040
+ return runAuthenticationStep({ context, options: target });
7041
+ case "connections":
7042
+ if (!await showActiveConnections(context)) {
7043
+ console.log(
7044
+ `No active connections found. Ask the user to connect or reconnect an app at ${CONNECTIONS_URL}. After they confirm, run this exact command:
7045
+ ${formatSetupCommand({ context, target })}
7046
+ Success means it prints the active connections table.`
7047
+ );
7048
+ throw new ZapierCliExitError(
7049
+ "Setup stopped until an app connection is active."
7050
+ );
7051
+ }
7052
+ console.log(
7053
+ "Show the user every app and connection title in the table above before continuing. Do not just report that connections were found."
7054
+ );
7055
+ return { step: "slack" };
7056
+ case "slack":
7057
+ case "slack-overwrite":
7058
+ case "slack-existing":
7059
+ case "slack-retry": {
7060
+ const { data: profile } = await context.imports.getProfile({});
7061
+ context.accountEmail = profile.email;
7062
+ return {
7063
+ step: await runSlackStep({
7064
+ context,
7065
+ step: target.step,
7066
+ connection: await findSlackConnection(context)
7067
+ })
7068
+ };
7069
+ }
7070
+ }
7071
+ }
7072
+ async function runAgentSetup({
7073
+ context,
7074
+ options
7075
+ }) {
7076
+ if (options.agent && options.nonInteractive)
7077
+ throw new ZapierCliValidationError(
7078
+ "Use --agent to inspect a step or --non-interactive to execute it, not both."
7079
+ );
7080
+ if (!options.agent && !options.nonInteractive)
7081
+ throw new ZapierCliValidationError(
7082
+ options.step ? "Use --agent to inspect the named step or --non-interactive to execute it." : "--answer, --flow, and --language require a setup step."
7083
+ );
7084
+ if (options.nonInteractive && !options.step)
7085
+ throw new ZapierCliValidationError(
7086
+ "--non-interactive requires --step. Start with setup --agent and follow its printed commands."
7087
+ );
7088
+ if (options.answer !== void 0 && !options.nonInteractive)
7089
+ throw new ZapierCliValidationError("--answer requires --non-interactive.");
7090
+ context.cwd = fs.realpathSync(context.cwd);
7091
+ context.projectLanguage = options.language ?? detectProjectLanguage({
7092
+ cwd: context.cwd,
7093
+ hasExistingProject: fs.existsSync(path.join(context.cwd, "package.json"))
7094
+ });
7095
+ let target = {
7096
+ step: options.step ?? "directory",
7097
+ flow: options.flow
7098
+ };
7099
+ let execute = options.nonInteractive === true;
7100
+ while (true) {
7101
+ const { step, flow } = target;
7102
+ if (step === "handoff") return printAgentCompletion({ context });
7103
+ if (!execute && UNPROMPTED_STEPS.includes(step))
7104
+ return printAgentStep({ context, target });
7105
+ const answer = createSetupAnswer({
7106
+ answer: execute ? options.answer : void 0,
7107
+ execute
7108
+ });
7109
+ context.createAnswer = () => answer;
7110
+ try {
7111
+ const next = await runSetupStep({ context, target: { step, flow } });
7112
+ if (options.language) context.projectLanguage = options.language;
7113
+ if (execute)
7114
+ for (const [operation, outcome] of Object.entries(
7115
+ context.outcomes ?? {}
7116
+ ))
7117
+ console.log(`${operation}: ${outcome}`);
7118
+ target = next;
7119
+ execute = false;
7120
+ } catch (error) {
7121
+ if (isZapierSetupQuestionSignal(error))
7122
+ return printAgentStep({ context, target, question: error.question });
7123
+ if (error instanceof ZapierCliUserCancellationError)
7124
+ return printAgentCompletion({ context, cancelled: true });
7125
+ throw error;
7126
+ }
7127
+ }
7128
+ }
6669
7129
 
6670
7130
  // src/plugins/setup/index.ts
6671
7131
  var getProfileRef = zapierSdk.declareMethod({ id: "getProfile" });
@@ -6690,10 +7150,19 @@ var setupPlugin = zapierSdk.defineMethod({
6690
7150
  supportsJsonOutput: false,
6691
7151
  inputSchema: SetupSchema,
6692
7152
  output: "raw",
6693
- run: async ({ imports }) => {
7153
+ run: async ({ imports, input }) => {
7154
+ if (input.agent || input.nonInteractive || [input.step, input.answer, input.flow, input.language].some(
7155
+ (value) => value !== void 0
7156
+ )) {
7157
+ await runAgentSetup({
7158
+ context: createWizardContext({ imports, agent: true }),
7159
+ options: input
7160
+ });
7161
+ return;
7162
+ }
6694
7163
  if (process.stdin.isTTY !== true || process.stdout.isTTY !== true) {
6695
- throw new ZapierCliExitError(
6696
- "`zapier-sdk setup` requires an interactive terminal."
7164
+ throw new ZapierCliValidationError(
7165
+ "`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."
6697
7166
  );
6698
7167
  }
6699
7168
  await runWizard(createWizardContext({ imports }));
@@ -7183,59 +7652,6 @@ var watchTriggerInboxCliPlugin = zapierSdk.defineMethod({
7183
7652
  return doWatch();
7184
7653
  }
7185
7654
  });
7186
- var createWorkflowRef = experimental.declareMethod({ id: "createWorkflow" });
7187
- function createPublicInputSchema(schema) {
7188
- const publicSchema = zod.z.object(
7189
- Object.fromEntries(
7190
- Object.entries(schema.shape).filter(
7191
- ([name]) => !experimental.INTERNAL_CREATE_WORKFLOW_FIELDS.includes(
7192
- name
7193
- )
7194
- )
7195
- )
7196
- );
7197
- return schema.description === void 0 ? publicSchema : publicSchema.describe(schema.description);
7198
- }
7199
- var CreateWorkflowCliOptionsSchema = createPublicInputSchema(
7200
- experimental.CreateWorkflowOptionsSchema
7201
- );
7202
- experimental.defineMethod({
7203
- namespace: "cli",
7204
- name: "createWorkflow",
7205
- imports: [createWorkflowRef],
7206
- type: "create",
7207
- itemType: "Workflow",
7208
- categories: ["code-workflow"],
7209
- stability: "experimental",
7210
- inputSchema: CreateWorkflowCliOptionsSchema,
7211
- outputSchema: experimental.CreateWorkflowResponseSchema,
7212
- skipOutputValidation: true,
7213
- output: "raw",
7214
- run: ({ imports, input }) => imports.createWorkflow({
7215
- ...input,
7216
- creationHarness: experimental.getAgent(),
7217
- creationAccessMethod: "sdk_cli"
7218
- })
7219
- });
7220
- var importWorkflowRef = experimental.declareMethod({ id: "importWorkflow" });
7221
- experimental.defineMethod({
7222
- namespace: "cli",
7223
- name: "importWorkflow",
7224
- imports: [importWorkflowRef],
7225
- type: "create",
7226
- itemType: "Workflow",
7227
- categories: ["code-workflow"],
7228
- stability: "experimental",
7229
- inputSchema: createPublicInputSchema(experimental.ImportWorkflowOptionsSchema),
7230
- outputSchema: experimental.ImportWorkflowResponseSchema,
7231
- skipOutputValidation: true,
7232
- output: "raw",
7233
- run: ({ imports, input }) => imports.importWorkflow({
7234
- ...input,
7235
- creationHarness: experimental.getAgent(),
7236
- creationAccessMethod: "sdk_cli"
7237
- })
7238
- });
7239
7655
  var activeNotices = /* @__PURE__ */ new Map();
7240
7656
  function collectDeprecationNotice(event) {
7241
7657
  if (event.type !== zapierSdk.DEPRECATION_NOTICE_EVENT) return;
@@ -7250,9 +7666,9 @@ function collectDeprecationNoticeAndForward(event, onEvent) {
7250
7666
  }
7251
7667
 
7252
7668
  // package.json with { type: 'json' }
7253
- var package_default = {
7669
+ var package_default2 = {
7254
7670
  name: "@zapier/zapier-sdk-cli",
7255
- version: "0.86.7"};
7671
+ version: "0.87.2"};
7256
7672
 
7257
7673
  // src/sdk.ts
7258
7674
  var warnedDeprecatedMethods = /* @__PURE__ */ new Set();
@@ -7318,7 +7734,7 @@ function createZapierCliSdk(options = {}) {
7318
7734
  const stackOptions = {
7319
7735
  ...sdkOptions,
7320
7736
  eventEmission: { ...sdkOptions.eventEmission, callContext: "cli" },
7321
- callerPackage: { name: package_default.name, version: package_default.version },
7737
+ callerPackage: { name: package_default2.name, version: package_default2.version },
7322
7738
  // Collected for the boxed stderr warning rendered in the exit path. The
7323
7739
  // box is additive: the SDK's own inline warn still fires, so the notice
7324
7740
  // cannot be hidden by any rendering path.
@@ -7343,12 +7759,6 @@ function createZapierCliSdk(options = {}) {
7343
7759
  }
7344
7760
  return sdk;
7345
7761
  }
7346
-
7347
- // package.json
7348
- var package_default2 = {
7349
- version: "0.86.7"};
7350
-
7351
- // src/telemetry/builders.ts
7352
7762
  function createCliBaseEvent(context = {}) {
7353
7763
  return {
7354
7764
  event_id: zapierSdk.generateEventId(),
@@ -7366,7 +7776,7 @@ function createCliBaseEvent(context = {}) {
7366
7776
  function buildCliCommandExecutedEvent({
7367
7777
  data,
7368
7778
  context = {},
7369
- cliVersion = package_default2.version
7779
+ cliVersion = package_default.version
7370
7780
  }) {
7371
7781
  const osInfo = zapierSdk.getOsInfo();
7372
7782
  const platformVersions = zapierSdk.getPlatformVersions();