@zapier/zapier-sdk-cli 0.32.4 → 0.34.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.
Files changed (37) hide show
  1. package/CHANGELOG.md +28 -0
  2. package/README.md +39 -2
  3. package/dist/cli.cjs +589 -110
  4. package/dist/cli.mjs +580 -102
  5. package/dist/index.cjs +531 -18
  6. package/dist/index.mjs +525 -15
  7. package/dist/package.json +3 -2
  8. package/dist/src/cli.js +11 -1
  9. package/dist/src/paths.d.ts +1 -0
  10. package/dist/src/paths.js +6 -0
  11. package/dist/src/plugins/index.d.ts +1 -0
  12. package/dist/src/plugins/index.js +1 -0
  13. package/dist/src/plugins/init/display.d.ts +9 -0
  14. package/dist/src/plugins/init/display.js +72 -0
  15. package/dist/src/plugins/init/index.d.ts +16 -0
  16. package/dist/src/plugins/init/index.js +61 -0
  17. package/dist/src/plugins/init/schemas.d.ts +8 -0
  18. package/dist/src/plugins/init/schemas.js +14 -0
  19. package/dist/src/plugins/init/steps.d.ts +39 -0
  20. package/dist/src/plugins/init/steps.js +141 -0
  21. package/dist/src/plugins/init/types.d.ts +31 -0
  22. package/dist/src/plugins/init/types.js +1 -0
  23. package/dist/src/plugins/init/utils.d.ts +48 -0
  24. package/dist/src/plugins/init/utils.js +135 -0
  25. package/dist/src/plugins/logout/index.js +1 -1
  26. package/dist/src/sdk.js +5 -2
  27. package/dist/src/utils/auth/login.js +33 -4
  28. package/dist/src/utils/package-manager-detector.d.ts +3 -1
  29. package/dist/src/utils/package-manager-detector.js +1 -0
  30. package/dist/src/utils/version-checker.js +1 -8
  31. package/dist/tsconfig.tsbuildinfo +1 -1
  32. package/package.json +6 -5
  33. package/templates/basic/AGENTS.md.hbs +15 -0
  34. package/templates/basic/README.md.hbs +21 -0
  35. package/templates/basic/package.json.hbs +17 -0
  36. package/templates/basic/src/index.ts +46 -0
  37. package/templates/basic/tsconfig.json +12 -0
package/dist/index.mjs CHANGED
@@ -1,20 +1,26 @@
1
- import { createFunction, OutputPropertySchema, DEFAULT_CONFIG_PATH, getOsInfo, getPlatformVersions, getCiPlatform, isCi, createZapierSdkWithoutRegistry, registryPlugin, getReleaseId, getCurrentTimestamp, generateEventId, ZapierValidationError, ZapierUnknownError, batch, toSnakeCase, isCredentialsObject, ZapierError } from '@zapier/zapier-sdk';
1
+ import * as cliLogin from '@zapier/zapier-sdk-cli-login';
2
+ import { logout, getConfigPath, getLoggedInUser, getPkceLoginConfig, AUTH_MODE_HEADER, getLoginStorageMode, updateLogin } from '@zapier/zapier-sdk-cli-login';
3
+ import { createFunction, OutputPropertySchema, DEFAULT_CONFIG_PATH, injectCliLogin, getOsInfo, getPlatformVersions, getCiPlatform, isCi, createZapierSdkWithoutRegistry, registryPlugin, getReleaseId, getCurrentTimestamp, generateEventId, ZapierValidationError, ZapierUnknownError, batch, toSnakeCase, ZapierError, isCredentialsObject } from '@zapier/zapier-sdk';
2
4
  import open from 'open';
3
5
  import crypto, { createHash } from 'crypto';
4
6
  import express from 'express';
5
7
  import pkceChallenge from 'pkce-challenge';
6
- import { logout, getConfigPath, getLoggedInUser, getPkceLoginConfig, AUTH_MODE_HEADER, updateLogin } from '@zapier/zapier-sdk-cli-login';
7
8
  import ora from 'ora';
8
- import chalk from 'chalk';
9
+ import chalk3 from 'chalk';
10
+ import inquirer from 'inquirer';
9
11
  import { z } from 'zod';
10
12
  import { startMcpServer } from '@zapier/zapier-sdk-mcp';
11
13
  import { buildSync } from 'esbuild';
12
14
  import * as fs from 'fs';
13
- import { promises, createWriteStream } from 'fs';
15
+ import { promises, createWriteStream, existsSync, readdirSync, rmSync, mkdirSync, writeFileSync, copyFileSync, readFileSync } from 'fs';
14
16
  import * as path from 'path';
15
- import { resolve, join, dirname, basename } from 'path';
17
+ import { resolve, join, dirname, basename, relative, extname } from 'path';
16
18
  import { mkdir, writeFile, access } from 'fs/promises';
17
19
  import * as ts from 'typescript';
20
+ import 'is-installed-globally';
21
+ import { execSync } from 'child_process';
22
+ import Handlebars from 'handlebars';
23
+ import { fileURLToPath } from 'url';
18
24
 
19
25
  // src/sdk.ts
20
26
  var LOGIN_PORTS = [49505, 50575, 52804, 55981, 61010, 63851];
@@ -29,6 +35,14 @@ var ZapierCliUserCancellationError = class extends ZapierCliError {
29
35
  this.exitCode = 0;
30
36
  }
31
37
  };
38
+ var ZapierCliExitError = class extends ZapierCliError {
39
+ constructor(message, exitCode = 1) {
40
+ super(message);
41
+ this.name = "ZapierCliExitError";
42
+ this.code = "ZAPIER_CLI_EXIT";
43
+ this.exitCode = exitCode;
44
+ }
45
+ };
32
46
 
33
47
  // src/utils/spinner.ts
34
48
  var spinPromise = async (promise, text) => {
@@ -48,20 +62,20 @@ var spinPromise = async (promise, text) => {
48
62
  };
49
63
  var log = {
50
64
  info: (message, ...args) => {
51
- console.log(chalk.blue("\u2139"), message, ...args);
65
+ console.log(chalk3.blue("\u2139"), message, ...args);
52
66
  },
53
67
  error: (message, ...args) => {
54
- console.error(chalk.red("\u2716"), message, ...args);
68
+ console.error(chalk3.red("\u2716"), message, ...args);
55
69
  },
56
70
  success: (message, ...args) => {
57
- console.log(chalk.green("\u2713"), message, ...args);
71
+ console.log(chalk3.green("\u2713"), message, ...args);
58
72
  },
59
73
  warn: (message, ...args) => {
60
- console.log(chalk.yellow("\u26A0"), message, ...args);
74
+ console.log(chalk3.yellow("\u26A0"), message, ...args);
61
75
  },
62
76
  debug: (message, ...args) => {
63
77
  if (process.env.DEBUG === "true" || process.argv.includes("--debug")) {
64
- console.log(chalk.gray("\u{1F41B}"), message, ...args);
78
+ console.log(chalk3.gray("\u{1F41B}"), message, ...args);
65
79
  }
66
80
  }
67
81
  };
@@ -168,7 +182,7 @@ var login = async ({
168
182
  const scope = ensureOfflineAccess(
169
183
  credentials?.scope || "internal credentials"
170
184
  );
171
- logout();
185
+ await logout();
172
186
  const availablePort = await findAvailablePort();
173
187
  const redirectUri = `http://localhost:${availablePort}/oauth`;
174
188
  log_default.info(`Using port ${availablePort} for OAuth callback`);
@@ -261,7 +275,32 @@ var login = async ({
261
275
  }
262
276
  }
263
277
  );
264
- updateLogin(data);
278
+ let targetStorage;
279
+ if (getLoginStorageMode() === "config") {
280
+ const { upgrade } = await inquirer.prompt([
281
+ {
282
+ type: "confirm",
283
+ name: "upgrade",
284
+ message: "Would you like to upgrade to system keychain storage? This is recommended to securely store your credentials. However, note that older SDK/CLI versions will NOT be able to read these credentials, so you will want to upgrade them to the latest version.",
285
+ default: true
286
+ }
287
+ ]);
288
+ targetStorage = upgrade ? "keychain" : "config";
289
+ } else {
290
+ targetStorage = "keychain";
291
+ }
292
+ try {
293
+ await updateLogin(data, { storage: targetStorage });
294
+ } catch (err) {
295
+ if (targetStorage === "keychain") {
296
+ log_default.warn(
297
+ `Could not store credentials in system keychain. Storing in plaintext at ${getConfigPath()}.`
298
+ );
299
+ await updateLogin(data, { storage: "config" });
300
+ } else {
301
+ throw err;
302
+ }
303
+ }
265
304
  log_default.info("Token exchange completed successfully");
266
305
  return data.access_token;
267
306
  };
@@ -272,7 +311,7 @@ var LoginSchema = z.object({
272
311
 
273
312
  // package.json
274
313
  var package_default = {
275
- version: "0.32.4"};
314
+ version: "0.34.1"};
276
315
 
277
316
  // src/telemetry/builders.ts
278
317
  function createCliBaseEvent(context = {}) {
@@ -411,7 +450,7 @@ var LogoutSchema = z.object({}).describe("Log out of your Zapier account");
411
450
 
412
451
  // src/plugins/logout/index.ts
413
452
  var logoutWithSdk = createFunction(async function logoutWithSdk2(_options) {
414
- logout();
453
+ await logout();
415
454
  console.log("\u2705 Successfully logged out");
416
455
  }, LogoutSchema);
417
456
  var logoutPlugin = () => ({
@@ -2040,13 +2079,484 @@ var cliOverridesPlugin = ({ context }) => {
2040
2079
  }
2041
2080
  };
2042
2081
  };
2082
+ var TEMPLATES = ["basic"];
2083
+ var InitSchema = z.object({
2084
+ projectName: z.string().min(1).describe("Name of the project directory to create"),
2085
+ skipPrompts: z.boolean().optional().describe("Skip all interactive prompts and accept all defaults")
2086
+ }).describe(
2087
+ "Create a new Zapier SDK project in a new directory with starter files"
2088
+ );
2089
+ function detectPackageManager(cwd = process.cwd()) {
2090
+ const ua = process.env.npm_config_user_agent;
2091
+ if (ua) {
2092
+ if (ua.includes("yarn")) return { name: "yarn", source: "runtime" };
2093
+ if (ua.includes("pnpm")) return { name: "pnpm", source: "runtime" };
2094
+ if (ua.includes("bun")) return { name: "bun", source: "runtime" };
2095
+ if (ua.includes("npm")) return { name: "npm", source: "runtime" };
2096
+ }
2097
+ const files = [
2098
+ ["pnpm-lock.yaml", "pnpm"],
2099
+ ["yarn.lock", "yarn"],
2100
+ ["bun.lockb", "bun"],
2101
+ ["package-lock.json", "npm"]
2102
+ ];
2103
+ for (const [file, name] of files) {
2104
+ if (existsSync(join(cwd, file))) {
2105
+ return { name, source: "lockfile" };
2106
+ }
2107
+ }
2108
+ return { name: "unknown", source: "fallback" };
2109
+ }
2110
+ function getDirentParentPath(entry) {
2111
+ const e = entry;
2112
+ const parent = e.parentPath ?? e.path;
2113
+ if (!parent)
2114
+ throw new Error(
2115
+ "readdirSync entry missing parentPath/path \u2014 unsupported Node version"
2116
+ );
2117
+ return parent;
2118
+ }
2119
+ function toProjectName(name) {
2120
+ return name.toLowerCase().replace(/[^a-z0-9-]/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "");
2121
+ }
2122
+ function validateInitOptions({
2123
+ rawName,
2124
+ cwd
2125
+ }) {
2126
+ const projectName = toProjectName(rawName);
2127
+ if (!projectName) {
2128
+ throw new Error(
2129
+ `"${rawName}" results in an empty project name. Provide a name with at least one letter or number.`
2130
+ );
2131
+ }
2132
+ const projectDir = join(cwd, projectName);
2133
+ if (existsSync(projectDir)) {
2134
+ const contents = readdirSync(projectDir);
2135
+ if (contents.length > 0) {
2136
+ throw new Error(
2137
+ `Directory "${projectName}" already exists and is not empty. Choose a different name.`
2138
+ );
2139
+ }
2140
+ }
2141
+ return { projectName, projectDir };
2142
+ }
2143
+ function createExec({ cwd }) {
2144
+ return (cmd) => execSync(cmd, {
2145
+ cwd,
2146
+ stdio: "inherit",
2147
+ shell: process.platform === "win32" ? "cmd.exe" : "/bin/sh"
2148
+ });
2149
+ }
2150
+ async function promptYesNo({
2151
+ message,
2152
+ defaultValue,
2153
+ skipPrompts
2154
+ }) {
2155
+ if (skipPrompts) return defaultValue;
2156
+ const { answer } = await inquirer.prompt([
2157
+ { type: "confirm", name: "answer", message, default: defaultValue }
2158
+ ]);
2159
+ return answer;
2160
+ }
2161
+ function getPackageManagerCommands({
2162
+ packageManager
2163
+ }) {
2164
+ return {
2165
+ installCmd: `${packageManager} install`,
2166
+ devCmd: packageManager === "yarn" ? "yarn dev" : `${packageManager} run dev`,
2167
+ devScript: packageManager === "bun" ? "bun src/index.ts" : "tsx src/index.ts",
2168
+ execCmd: {
2169
+ npm: "npx",
2170
+ yarn: "yarn dlx",
2171
+ pnpm: "pnpm dlx",
2172
+ bun: "bunx"
2173
+ }[packageManager]
2174
+ };
2175
+ }
2176
+ function isTemplateFile(name) {
2177
+ return extname(name) === ".hbs";
2178
+ }
2179
+ function getDestRelPath(relPath, isTemplate) {
2180
+ return isTemplate ? relPath.slice(0, -extname(relPath).length) : relPath;
2181
+ }
2182
+ function renderTemplate(srcPath, variables) {
2183
+ const source = readFileSync(srcPath, "utf-8");
2184
+ const template = Handlebars.compile(source, { noEscape: true });
2185
+ return template(variables);
2186
+ }
2187
+ function buildTemplateVariables({
2188
+ projectName,
2189
+ packageManager
2190
+ }) {
2191
+ const { execCmd, devScript, devCmd } = getPackageManagerCommands({
2192
+ packageManager
2193
+ });
2194
+ return {
2195
+ projectName,
2196
+ execCmd,
2197
+ devScript,
2198
+ devCmd,
2199
+ includeTsx: packageManager !== "bun"
2200
+ };
2201
+ }
2202
+ function cleanupProject({ projectDir }) {
2203
+ console.log("\n" + chalk3.yellow("!") + " Cleaning up...");
2204
+ rmSync(projectDir, { recursive: true, force: true });
2205
+ }
2206
+ async function withInterruptCleanup(cleanup, fn) {
2207
+ if (!cleanup) return fn();
2208
+ const handler = () => {
2209
+ cleanup();
2210
+ process.removeListener("SIGINT", handler);
2211
+ process.kill(process.pid, "SIGINT");
2212
+ };
2213
+ process.on("SIGINT", handler);
2214
+ try {
2215
+ return await fn();
2216
+ } finally {
2217
+ process.removeListener("SIGINT", handler);
2218
+ }
2219
+ }
2220
+ function createProjectDir({
2221
+ projectDir,
2222
+ projectName
2223
+ }) {
2224
+ try {
2225
+ mkdirSync(projectDir, { recursive: true });
2226
+ } catch (err) {
2227
+ if (err instanceof Error) {
2228
+ const code = err.code;
2229
+ if (code === "EACCES") {
2230
+ throw new Error(
2231
+ `Permission denied creating "${projectName}". Check directory permissions.`
2232
+ );
2233
+ }
2234
+ if (code === "ENOSPC") {
2235
+ throw new Error("No space left on device.");
2236
+ }
2237
+ }
2238
+ throw err;
2239
+ }
2240
+ }
2241
+ var TEMPLATES_DIR = fileURLToPath(
2242
+ new URL("../templates", import.meta.url)
2243
+ );
2244
+
2245
+ // src/plugins/init/steps.ts
2246
+ var DEFAULT_TEMPLATE = "basic";
2247
+ function getTemplateDir({
2248
+ template = DEFAULT_TEMPLATE
2249
+ } = {}) {
2250
+ const dirPath = join(TEMPLATES_DIR, template);
2251
+ if (!existsSync(dirPath)) {
2252
+ throw new Error(
2253
+ `Template "${template}" not found at ${dirPath}. Available templates: ${TEMPLATES.join(", ")}`
2254
+ );
2255
+ }
2256
+ return dirPath;
2257
+ }
2258
+ function scaffoldFiles({
2259
+ projectDir,
2260
+ templatesDir,
2261
+ variables = {},
2262
+ displayHooks
2263
+ }) {
2264
+ const entries = readdirSync(templatesDir, {
2265
+ withFileTypes: true,
2266
+ recursive: true
2267
+ });
2268
+ const files = entries.filter((e) => e.isFile());
2269
+ if (files.length === 0) {
2270
+ throw new Error(`Template directory "${templatesDir}" contains no files.`);
2271
+ }
2272
+ for (const entry of files) {
2273
+ const srcPath = join(getDirentParentPath(entry), entry.name);
2274
+ const relPath = relative(templatesDir, srcPath);
2275
+ const isTemplate = isTemplateFile(entry.name);
2276
+ const destRelPath = getDestRelPath(relPath, isTemplate);
2277
+ const destPath = join(projectDir, destRelPath);
2278
+ mkdirSync(dirname(destPath), { recursive: true });
2279
+ if (isTemplate) {
2280
+ writeFileSync(destPath, renderTemplate(srcPath, variables));
2281
+ } else {
2282
+ copyFileSync(srcPath, destPath);
2283
+ }
2284
+ displayHooks?.onItemComplete?.(destRelPath);
2285
+ }
2286
+ }
2287
+ function scaffoldProject({
2288
+ projectDir,
2289
+ projectName,
2290
+ packageManager,
2291
+ template,
2292
+ displayHooks
2293
+ }) {
2294
+ const variables = buildTemplateVariables({ projectName, packageManager });
2295
+ const templatesDir = getTemplateDir({ template });
2296
+ createProjectDir({ projectDir, projectName });
2297
+ scaffoldFiles({ projectDir, templatesDir, variables, displayHooks });
2298
+ }
2299
+ async function runStep({
2300
+ step,
2301
+ stepNumber,
2302
+ totalSteps,
2303
+ skipPrompts,
2304
+ displayHooks
2305
+ }) {
2306
+ if (step.askConfirmation) {
2307
+ const should = await promptYesNo({
2308
+ message: step.description,
2309
+ defaultValue: true,
2310
+ skipPrompts
2311
+ });
2312
+ if (!should) return false;
2313
+ }
2314
+ displayHooks?.onStepStart({
2315
+ description: step.description,
2316
+ stepNumber,
2317
+ totalSteps,
2318
+ command: step.command,
2319
+ skipPrompts
2320
+ });
2321
+ try {
2322
+ await step.run();
2323
+ displayHooks?.onStepSuccess({ stepNumber, totalSteps });
2324
+ return true;
2325
+ } catch (err) {
2326
+ step.cleanup?.();
2327
+ displayHooks?.onStepError({
2328
+ description: step.description,
2329
+ command: step.command,
2330
+ err
2331
+ });
2332
+ return false;
2333
+ }
2334
+ }
2335
+ function getInitSteps({
2336
+ projectDir,
2337
+ projectName,
2338
+ packageManager,
2339
+ template,
2340
+ displayHooks
2341
+ }) {
2342
+ if (template !== void 0 && !TEMPLATES.includes(template)) {
2343
+ throw new Error(
2344
+ `Unknown template "${template}". Available templates: ${TEMPLATES.join(", ")}`
2345
+ );
2346
+ }
2347
+ const { installCmd, execCmd, devCmd } = getPackageManagerCommands({
2348
+ packageManager
2349
+ });
2350
+ const exec = createExec({ cwd: projectDir });
2351
+ return [
2352
+ {
2353
+ id: "scaffold",
2354
+ description: "Scaffold project files",
2355
+ cleanup: () => cleanupProject({ projectDir }),
2356
+ run: () => scaffoldProject({
2357
+ projectDir,
2358
+ projectName,
2359
+ packageManager,
2360
+ template,
2361
+ displayHooks
2362
+ })
2363
+ },
2364
+ {
2365
+ id: "install-deps",
2366
+ description: "Install dependencies",
2367
+ askConfirmation: true,
2368
+ command: installCmd,
2369
+ run: () => exec(installCmd)
2370
+ },
2371
+ {
2372
+ id: "login",
2373
+ description: "Log in to Zapier",
2374
+ askConfirmation: true,
2375
+ command: `${execCmd} zapier-sdk login`,
2376
+ run: () => exec(`${execCmd} zapier-sdk login`)
2377
+ },
2378
+ {
2379
+ id: "run",
2380
+ description: "Run your app",
2381
+ askConfirmation: true,
2382
+ command: devCmd,
2383
+ run: () => exec(devCmd)
2384
+ }
2385
+ ];
2386
+ }
2387
+ function buildNextSteps({
2388
+ projectName,
2389
+ leftoverSteps,
2390
+ execCmd
2391
+ }) {
2392
+ return [
2393
+ {
2394
+ description: "Change into your project directory",
2395
+ command: `cd ${projectName}`
2396
+ },
2397
+ ...leftoverSteps.map(({ description, command }) => ({
2398
+ description,
2399
+ command
2400
+ })),
2401
+ {
2402
+ description: "Search for an app to integrate",
2403
+ command: `${execCmd} zapier-sdk list-apps --search "<app name>"`
2404
+ },
2405
+ {
2406
+ description: "Add an app and generate TypeScript types",
2407
+ command: `${execCmd} zapier-sdk add <app-key>`
2408
+ }
2409
+ ];
2410
+ }
2411
+ function createConsoleDisplayHooks() {
2412
+ return {
2413
+ onItemComplete: (message) => console.log(" " + chalk3.green("\u2713") + " " + chalk3.dim(message)),
2414
+ onWarn: (message) => console.warn(chalk3.yellow("!") + " " + message),
2415
+ onStepStart: ({
2416
+ description,
2417
+ stepNumber,
2418
+ totalSteps,
2419
+ command,
2420
+ skipPrompts
2421
+ }) => {
2422
+ const progressMessage = `${description}...`;
2423
+ const stepCounter = chalk3.dim(`${stepNumber}/${totalSteps}`);
2424
+ if (skipPrompts) {
2425
+ console.log(
2426
+ "\n" + chalk3.bold(`\u276F ${progressMessage}`) + " " + stepCounter + "\n"
2427
+ );
2428
+ } else {
2429
+ console.log(
2430
+ chalk3.dim("\u2192") + " " + progressMessage + " " + stepCounter
2431
+ );
2432
+ }
2433
+ if (command) {
2434
+ console.log(" " + chalk3.cyan(`$ ${command}`));
2435
+ }
2436
+ },
2437
+ onStepSuccess: ({ stepNumber, totalSteps }) => console.log(
2438
+ "\n" + chalk3.green("\u2713") + " " + chalk3.dim(`Step ${stepNumber}/${totalSteps} complete`) + "\n"
2439
+ ),
2440
+ onStepError: ({ description, command, err }) => {
2441
+ const detail = err instanceof Error && err.message ? `
2442
+ ${chalk3.dim(err.message)}` : "";
2443
+ const hint = command ? `
2444
+ ${chalk3.dim("run manually:")} ${chalk3.cyan(`$ ${command}`)}` : "";
2445
+ console.error(
2446
+ `
2447
+ ${chalk3.red("\u2716")} ${chalk3.bold(description)}${chalk3.dim(" failed")}${detail}${hint}`
2448
+ );
2449
+ }
2450
+ };
2451
+ }
2452
+ function displaySummaryAndNextSteps({
2453
+ projectName,
2454
+ steps,
2455
+ completedSetupStepIds,
2456
+ packageManager
2457
+ }) {
2458
+ const formatStatus = (complete) => ({
2459
+ icon: complete ? chalk3.green("\u2713") : chalk3.yellow("!"),
2460
+ text: complete ? chalk3.green("Setup complete") : chalk3.yellow("Setup interrupted")
2461
+ });
2462
+ const formatNextStep = (step, i) => " " + chalk3.dim(`${i + 1}.`) + " " + chalk3.bold(step.description);
2463
+ const formatCommand = (cmd) => " " + chalk3.cyan(`$ ${cmd}`);
2464
+ const formatCompletedStep = (step) => " " + chalk3.green("\u2713") + " " + step.description;
2465
+ const { execCmd } = getPackageManagerCommands({ packageManager });
2466
+ const leftoverSteps = steps.filter(
2467
+ (s) => !completedSetupStepIds.includes(s.id)
2468
+ );
2469
+ const isComplete = leftoverSteps.length === 0;
2470
+ const status = formatStatus(isComplete);
2471
+ console.log("\n" + chalk3.bold("\u276F Summary") + "\n");
2472
+ console.log(" " + chalk3.dim("Project") + " " + chalk3.bold(projectName));
2473
+ console.log(
2474
+ " " + chalk3.dim("Status") + " " + status.icon + " " + status.text
2475
+ );
2476
+ const completedSteps = steps.filter(
2477
+ (s) => completedSetupStepIds.includes(s.id)
2478
+ );
2479
+ if (completedSteps.length > 0) {
2480
+ console.log();
2481
+ for (const step of completedSteps) console.log(formatCompletedStep(step));
2482
+ }
2483
+ const nextSteps = buildNextSteps({ projectName, leftoverSteps, execCmd });
2484
+ console.log("\n" + chalk3.bold("\u276F Next Steps") + "\n");
2485
+ nextSteps.forEach((step, i) => {
2486
+ console.log(formatNextStep(step, i));
2487
+ if (step.command) console.log(formatCommand(step.command));
2488
+ console.log();
2489
+ });
2490
+ }
2491
+
2492
+ // src/plugins/init/index.ts
2493
+ var initPlugin = () => {
2494
+ const init = createFunction(async function init2(options) {
2495
+ const { projectName: rawName, skipPrompts = false } = options;
2496
+ const cwd = process.cwd();
2497
+ const { projectName, projectDir } = validateInitOptions({ rawName, cwd });
2498
+ const displayHooks = createConsoleDisplayHooks();
2499
+ const packageManagerInfo = detectPackageManager(cwd);
2500
+ if (packageManagerInfo.name === "unknown") {
2501
+ displayHooks.onWarn(
2502
+ "Could not detect package manager, defaulting to npm."
2503
+ );
2504
+ }
2505
+ const packageManager = packageManagerInfo.name === "unknown" ? "npm" : packageManagerInfo.name;
2506
+ const steps = getInitSteps({
2507
+ projectDir,
2508
+ projectName,
2509
+ packageManager,
2510
+ displayHooks
2511
+ });
2512
+ const completedSetupStepIds = [];
2513
+ for (let i = 0; i < steps.length; i++) {
2514
+ const step = steps[i];
2515
+ const succeeded = await withInterruptCleanup(
2516
+ step.cleanup,
2517
+ () => runStep({
2518
+ step,
2519
+ stepNumber: i + 1,
2520
+ totalSteps: steps.length,
2521
+ skipPrompts,
2522
+ displayHooks
2523
+ })
2524
+ );
2525
+ if (!succeeded) break;
2526
+ completedSetupStepIds.push(step.id);
2527
+ }
2528
+ if (completedSetupStepIds.length === 0) {
2529
+ throw new ZapierCliExitError(
2530
+ "Project setup failed \u2014 no steps completed."
2531
+ );
2532
+ }
2533
+ displaySummaryAndNextSteps({
2534
+ projectName,
2535
+ steps,
2536
+ completedSetupStepIds,
2537
+ packageManager
2538
+ });
2539
+ }, InitSchema);
2540
+ return {
2541
+ init,
2542
+ context: {
2543
+ meta: {
2544
+ init: {
2545
+ categories: ["utility"],
2546
+ inputSchema: InitSchema
2547
+ }
2548
+ }
2549
+ }
2550
+ };
2551
+ };
2043
2552
 
2044
2553
  // src/sdk.ts
2554
+ injectCliLogin(cliLogin);
2045
2555
  function createZapierCliSdk(options = {}) {
2046
2556
  return createZapierSdkWithoutRegistry({
2047
2557
  ...options,
2048
2558
  eventEmission: { ...options.eventEmission, callContext: "cli" }
2049
- }).addPlugin(generateAppTypesPlugin).addPlugin(buildManifestPlugin).addPlugin(bundleCodePlugin).addPlugin(getLoginConfigPathPlugin).addPlugin(addPlugin).addPlugin(feedbackPlugin).addPlugin(curlPlugin).addPlugin(mcpPlugin).addPlugin(loginPlugin).addPlugin(logoutPlugin).addPlugin(cliOverridesPlugin).addPlugin(registryPlugin);
2559
+ }).addPlugin(generateAppTypesPlugin).addPlugin(buildManifestPlugin).addPlugin(bundleCodePlugin).addPlugin(getLoginConfigPathPlugin).addPlugin(addPlugin).addPlugin(feedbackPlugin).addPlugin(curlPlugin).addPlugin(initPlugin).addPlugin(mcpPlugin).addPlugin(loginPlugin).addPlugin(logoutPlugin).addPlugin(cliOverridesPlugin).addPlugin(registryPlugin);
2050
2560
  }
2051
2561
 
2052
2562
  // src/utils/cli-options.ts
package/dist/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zapier/zapier-sdk-cli",
3
- "version": "0.32.4",
3
+ "version": "0.34.1",
4
4
  "description": "Command line interface for Zapier SDK",
5
5
  "main": "dist/index.cjs",
6
6
  "module": "dist/index.mjs",
@@ -49,6 +49,7 @@
49
49
  "files": [
50
50
  "dist",
51
51
  "bin",
52
+ "templates",
52
53
  "README.md",
53
54
  "CHANGELOG.md",
54
55
  "CLAUDE.md"
@@ -63,9 +64,9 @@
63
64
  "chalk": "^5.3.0",
64
65
  "cli-table3": "^0.6.5",
65
66
  "commander": "^12.0.0",
66
- "conf": "^14.0.0",
67
67
  "esbuild": "^0.25.5",
68
68
  "express": "^5.1.0",
69
+ "handlebars": "^4.7.8",
69
70
  "inquirer": "^12.6.3",
70
71
  "is-installed-globally": "^1.0.0",
71
72
  "jsonwebtoken": "^9.0.2",
package/dist/src/cli.js CHANGED
@@ -6,6 +6,7 @@ import packageJson from "../package.json" with { type: "json" };
6
6
  import { ZapierCliError } from "./utils/errors";
7
7
  import { checkAndNotifyUpdates } from "./utils/version-checker";
8
8
  import { ReservedCliParameter, getReservedCliOption, } from "./utils/cli-options";
9
+ const EXIT_GRACE_PERIOD_MS = 500;
9
10
  const program = new Command();
10
11
  const versionOption = getReservedCliOption(ReservedCliParameter.Version);
11
12
  const helpOption = getReservedCliOption(ReservedCliParameter.Help);
@@ -116,6 +117,15 @@ program.exitOverride();
116
117
  }
117
118
  // Wait for version checking to complete
118
119
  await versionCheckPromise;
119
- // Use exitCode instead of exit() to allow stdout to flush when piped
120
+ // Flush the exit telemetry event before we force-terminate. The async
121
+ // transport needs an explicit await because process.on("exit") only
122
+ // allows synchronous work.
123
+ await sdk.getContext().eventEmission.close(exitCode);
124
+ // Force exit after a short grace period. We can't rely on process.exitCode
125
+ // alone because background handles (stdin, pending file closes) keep the
126
+ // event loop alive indefinitely. The unref'd timer won't prevent natural
127
+ // exit, but guarantees the process terminates if the loop doesn't drain.
128
+ const exitTimeout = setTimeout(() => process.exit(exitCode), EXIT_GRACE_PERIOD_MS);
129
+ exitTimeout.unref();
120
130
  process.exitCode = exitCode;
121
131
  })();
@@ -0,0 +1 @@
1
+ export declare const TEMPLATES_DIR: string;
@@ -0,0 +1,6 @@
1
+ import { fileURLToPath } from "url";
2
+ // src/ and dist/ are both one level below the package root, so "../templates"
3
+ // resolves correctly in both the unbundled source context and the bundled
4
+ // dist/index.mjs runtime. Moving this expression into a deeper file would
5
+ // break one of the two contexts.
6
+ export const TEMPLATES_DIR = fileURLToPath(new URL("../templates", import.meta.url));
@@ -9,3 +9,4 @@ export { buildManifestPlugin } from "./buildManifest";
9
9
  export { feedbackPlugin } from "./feedback";
10
10
  export { curlPlugin } from "./curl";
11
11
  export { cliOverridesPlugin } from "./cliOverrides";
12
+ export { initPlugin } from "./init";
@@ -9,3 +9,4 @@ export { buildManifestPlugin } from "./buildManifest";
9
9
  export { feedbackPlugin } from "./feedback";
10
10
  export { curlPlugin } from "./curl";
11
11
  export { cliOverridesPlugin } from "./cliOverrides";
12
+ export { initPlugin } from "./init";