@zapier/zapier-sdk-cli 0.8.3 → 0.9.0

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 (45) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/README.md +35 -51
  3. package/dist/cli.cjs +750 -346
  4. package/dist/cli.mjs +751 -347
  5. package/dist/index.cjs +726 -336
  6. package/dist/index.mjs +727 -337
  7. package/dist/package.json +1 -1
  8. package/dist/src/plugins/add/ast-generator.d.ts +37 -0
  9. package/dist/src/plugins/add/ast-generator.js +403 -0
  10. package/dist/src/plugins/add/index.d.ts +13 -0
  11. package/dist/src/plugins/add/index.js +122 -0
  12. package/dist/src/plugins/add/schemas.d.ts +18 -0
  13. package/dist/src/plugins/add/schemas.js +19 -0
  14. package/dist/src/plugins/getLoginConfigPath/index.d.ts +15 -0
  15. package/dist/src/plugins/getLoginConfigPath/index.js +19 -0
  16. package/dist/src/plugins/getLoginConfigPath/schemas.d.ts +3 -0
  17. package/dist/src/plugins/getLoginConfigPath/schemas.js +5 -0
  18. package/dist/src/plugins/index.d.ts +2 -2
  19. package/dist/src/plugins/index.js +2 -2
  20. package/dist/src/sdk.js +3 -3
  21. package/dist/src/utils/cli-generator.js +15 -0
  22. package/dist/tsconfig.tsbuildinfo +1 -1
  23. package/package.json +3 -3
  24. package/src/plugins/add/ast-generator.ts +777 -0
  25. package/src/plugins/add/index.test.ts +58 -0
  26. package/src/plugins/add/index.ts +187 -0
  27. package/src/plugins/add/schemas.ts +26 -0
  28. package/src/plugins/getLoginConfigPath/index.ts +45 -0
  29. package/src/plugins/getLoginConfigPath/schemas.ts +10 -0
  30. package/src/plugins/index.ts +2 -2
  31. package/src/sdk.ts +4 -4
  32. package/src/utils/cli-generator.ts +22 -0
  33. package/tsup.config.ts +1 -1
  34. package/dist/src/plugins/generateTypes/index.d.ts +0 -21
  35. package/dist/src/plugins/generateTypes/index.js +0 -312
  36. package/dist/src/plugins/generateTypes/schemas.d.ts +0 -18
  37. package/dist/src/plugins/generateTypes/schemas.js +0 -14
  38. package/dist/src/plugins/getConfigPath/index.d.ts +0 -15
  39. package/dist/src/plugins/getConfigPath/index.js +0 -19
  40. package/dist/src/plugins/getConfigPath/schemas.d.ts +0 -3
  41. package/dist/src/plugins/getConfigPath/schemas.js +0 -5
  42. package/src/plugins/generateTypes/index.ts +0 -444
  43. package/src/plugins/generateTypes/schemas.ts +0 -23
  44. package/src/plugins/getConfigPath/index.ts +0 -42
  45. package/src/plugins/getConfigPath/schemas.ts +0 -8
@@ -0,0 +1,58 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import { AddSchema } from "./schemas";
3
+ import { DEFAULT_CONFIG_PATH } from "@zapier/zapier-sdk";
4
+
5
+ describe("Add plugin", () => {
6
+ describe("schema validation", () => {
7
+ it("should accept valid options with authenticationIds", () => {
8
+ const result = AddSchema.safeParse({
9
+ appKeys: ["slack", "gmail"],
10
+ authenticationIds: ["123", "456"],
11
+ configPath: DEFAULT_CONFIG_PATH,
12
+ typesOutput: "src/types",
13
+ });
14
+
15
+ expect(result.success).toBe(true);
16
+ if (result.success) {
17
+ expect(result.data.authenticationIds).toEqual(["123", "456"]);
18
+ }
19
+ });
20
+
21
+ it("should accept options without authenticationIds", () => {
22
+ const result = AddSchema.safeParse({
23
+ appKeys: ["slack"],
24
+ });
25
+
26
+ expect(result.success).toBe(true);
27
+ if (result.success) {
28
+ expect(result.data.authenticationIds).toBeUndefined();
29
+ }
30
+ });
31
+
32
+ it("should reject invalid authenticationIds type", () => {
33
+ const result = AddSchema.safeParse({
34
+ appKeys: ["slack"],
35
+ authenticationIds: "123", // Should be array
36
+ });
37
+
38
+ expect(result.success).toBe(false);
39
+ });
40
+
41
+ it("should reject authenticationIds with non-string items", () => {
42
+ const result = AddSchema.safeParse({
43
+ appKeys: ["slack"],
44
+ authenticationIds: [123, 456], // Should be strings
45
+ });
46
+
47
+ expect(result.success).toBe(false);
48
+ });
49
+
50
+ it("should require at least one appKey", () => {
51
+ const result = AddSchema.safeParse({
52
+ appKeys: [],
53
+ });
54
+
55
+ expect(result.success).toBe(false);
56
+ });
57
+ });
58
+ });
@@ -0,0 +1,187 @@
1
+ import type {
2
+ Plugin,
3
+ GetSdkType,
4
+ GetContextType,
5
+ ListActionsPluginProvides,
6
+ ListAppsPluginProvides,
7
+ ListInputFieldsPluginProvides,
8
+ ListAuthenticationsPluginProvides,
9
+ ManifestPluginProvides,
10
+ } from "@zapier/zapier-sdk";
11
+ import { createFunction } from "@zapier/zapier-sdk";
12
+ import { AddSchema, type AddOptions } from "./schemas";
13
+ import { AstTypeGenerator } from "./ast-generator";
14
+ import { mkdir, access, writeFile } from "fs/promises";
15
+ import { resolve, join } from "path";
16
+
17
+ /**
18
+ * Detect the best default directory for TypeScript types
19
+ * Looks for src or lib directories, falls back to current directory
20
+ */
21
+ async function detectTypesOutputDirectory(): Promise<string> {
22
+ // Check for common source directories in priority order
23
+ const candidates = ["src", "lib"];
24
+
25
+ for (const candidate of candidates) {
26
+ try {
27
+ await access(candidate);
28
+ return join(candidate, "zapier", "apps");
29
+ } catch {
30
+ // Directory doesn't exist, continue to next candidate
31
+ }
32
+ }
33
+
34
+ // Fall back to current directory
35
+ return "./zapier/apps/";
36
+ }
37
+ export interface AddPluginProvides {
38
+ add: (options: AddOptions) => Promise<void>;
39
+ context: {
40
+ meta: {
41
+ add: {
42
+ inputSchema: typeof AddSchema;
43
+ };
44
+ };
45
+ };
46
+ }
47
+
48
+ export const addPlugin: Plugin<
49
+ GetSdkType<
50
+ ListAppsPluginProvides &
51
+ ListActionsPluginProvides &
52
+ ListInputFieldsPluginProvides &
53
+ ListAuthenticationsPluginProvides &
54
+ ManifestPluginProvides
55
+ >,
56
+ GetContextType<ManifestPluginProvides>,
57
+ AddPluginProvides
58
+ > = ({ sdk, context }) => {
59
+ const add = createFunction(async function add(options: AddOptions) {
60
+ const {
61
+ appKeys,
62
+ authenticationIds,
63
+ configPath,
64
+ typesOutput = await detectTypesOutputDirectory(),
65
+ } = options;
66
+
67
+ const resolvedTypesOutput = resolve(typesOutput);
68
+
69
+ // Ensure types output directory exists
70
+ await mkdir(resolvedTypesOutput, { recursive: true });
71
+
72
+ // Get apps using listApps (which respects existing manifest for version locking)
73
+ console.log(`📦 Looking up ${appKeys.length} app(s)...`);
74
+ const appsIterator = sdk.listApps({ appKeys }).items();
75
+ const apps = [];
76
+ for await (const app of appsIterator) {
77
+ apps.push(app);
78
+ }
79
+
80
+ if (apps.length === 0) {
81
+ console.warn("⚠️ No apps found");
82
+ return;
83
+ }
84
+
85
+ // Fetch authentications if provided
86
+ let authentications = [];
87
+ if (authenticationIds && authenticationIds.length > 0) {
88
+ console.log(
89
+ `🔐 Looking up ${authenticationIds.length} authentication(s)...`,
90
+ );
91
+ const authsIterator = sdk
92
+ .listAuthentications({ authenticationIds })
93
+ .items();
94
+ for await (const auth of authsIterator) {
95
+ authentications.push(auth);
96
+ }
97
+ console.log(`🔐 Found ${authentications.length} authentication(s)`);
98
+ }
99
+
100
+ // Process each app
101
+ for (const app of apps) {
102
+ console.log(`📦 Adding ${app.key}...`);
103
+
104
+ try {
105
+ // Extract implementation name and version from current_implementation_id
106
+ const currentImplementationId = app.current_implementation_id;
107
+ const [implementationName, version] =
108
+ currentImplementationId.split("@");
109
+
110
+ if (!implementationName || !version) {
111
+ console.warn(
112
+ `⚠️ Invalid implementation ID format for '${app.key}': ${currentImplementationId}. Expected format: <implementationName>@<version>. Skipping...`,
113
+ );
114
+ continue;
115
+ }
116
+
117
+ // Update manifest using manifest plugin's function
118
+ const [manifestKey] = await context.updateManifestEntry(
119
+ app.key,
120
+ {
121
+ implementationName,
122
+ version,
123
+ },
124
+ configPath,
125
+ );
126
+
127
+ console.log(
128
+ `📝 Locked ${app.key} to ${implementationName}@${version} using key '${manifestKey}'`,
129
+ );
130
+
131
+ // Find matching authentication for this app if authentications were provided
132
+ let authenticationId: number | undefined;
133
+ if (authentications.length > 0) {
134
+ // Match authentication by app_key
135
+ const matchingAuth = authentications.find((auth) => {
136
+ return auth.app_key === app.key;
137
+ });
138
+
139
+ if (matchingAuth) {
140
+ authenticationId = matchingAuth.id;
141
+ console.log(
142
+ `🔐 Using authentication ${authenticationId} (${matchingAuth.title}) for ${app.key}`,
143
+ );
144
+ } else {
145
+ console.warn(`⚠️ No matching authentication found for ${app.key}`);
146
+ }
147
+ }
148
+
149
+ // Generate types using the manifest key for consistency
150
+ const typesPath = join(resolvedTypesOutput, `${manifestKey}.d.ts`);
151
+
152
+ try {
153
+ // Use AST-based generator directly
154
+ const generator = new AstTypeGenerator();
155
+ const typeDefinitions = await generator.generateTypes({
156
+ appKey: manifestKey,
157
+ authenticationId,
158
+ sdk,
159
+ });
160
+
161
+ // Write to file
162
+ await writeFile(typesPath, typeDefinitions, "utf8");
163
+ console.log(`🔧 Generated types for ${manifestKey} at ${typesPath}`);
164
+ } catch (error) {
165
+ console.warn(`⚠️ Failed to generate types for ${app.key}: ${error}`);
166
+ // Continue even if type generation fails
167
+ }
168
+ } catch (error) {
169
+ console.warn(`⚠️ Failed to process ${app.key}: ${error}`);
170
+ }
171
+ }
172
+
173
+ console.log(`✅ Added ${apps.length} app(s) to manifest`);
174
+ }, AddSchema);
175
+
176
+ return {
177
+ add,
178
+ context: {
179
+ meta: {
180
+ add: {
181
+ categories: ["utility"],
182
+ inputSchema: AddSchema,
183
+ },
184
+ },
185
+ },
186
+ };
187
+ };
@@ -0,0 +1,26 @@
1
+ import { z } from "zod";
2
+ import { DEFAULT_CONFIG_PATH } from "@zapier/zapier-sdk";
3
+
4
+ export const AddSchema = z.object({
5
+ appKeys: z
6
+ .array(z.string().min(1, "App key cannot be empty"))
7
+ .min(1, "At least one app key is required"),
8
+ authenticationIds: z
9
+ .array(z.string())
10
+ .optional()
11
+ .describe("Authentication IDs to use for type generation"),
12
+ configPath: z
13
+ .string()
14
+ .optional()
15
+ .describe(
16
+ `Path to Zapier config file (defaults to '${DEFAULT_CONFIG_PATH}')`,
17
+ ),
18
+ typesOutput: z
19
+ .string()
20
+ .optional()
21
+ .describe(
22
+ "Directory for TypeScript type files (defaults to (src|lib|.)/zapier/apps/)",
23
+ ),
24
+ });
25
+
26
+ export type AddOptions = z.infer<typeof AddSchema>;
@@ -0,0 +1,45 @@
1
+ import type { Plugin } from "@zapier/zapier-sdk";
2
+ import {
3
+ GetLoginConfigPathSchema,
4
+ type GetLoginConfigPathOptions,
5
+ } from "./schemas";
6
+ import { createFunction } from "@zapier/zapier-sdk";
7
+ import { getConfigPath } from "@zapier/zapier-sdk-cli-login";
8
+
9
+ export interface GetLoginConfigPathPluginProvides {
10
+ getLoginConfigPath: (options?: GetLoginConfigPathOptions) => Promise<string>;
11
+ context: {
12
+ meta: {
13
+ getLoginConfigPath: {
14
+ inputSchema: typeof GetLoginConfigPathSchema;
15
+ };
16
+ };
17
+ };
18
+ }
19
+
20
+ export const getLoginConfigPathPlugin: Plugin<
21
+ {}, // requires no existing SDK methods
22
+ {}, // requires no context
23
+ GetLoginConfigPathPluginProvides
24
+ > = () => {
25
+ const getLoginConfigPathWithSdk = createFunction(
26
+ async function getLoginConfigPathWithSdk(
27
+ _options?: GetLoginConfigPathOptions,
28
+ ): Promise<string> {
29
+ return getConfigPath();
30
+ },
31
+ GetLoginConfigPathSchema,
32
+ );
33
+
34
+ return {
35
+ getLoginConfigPath: getLoginConfigPathWithSdk,
36
+ context: {
37
+ meta: {
38
+ getLoginConfigPath: {
39
+ categories: ["utility"],
40
+ inputSchema: GetLoginConfigPathSchema,
41
+ },
42
+ },
43
+ },
44
+ };
45
+ };
@@ -0,0 +1,10 @@
1
+ import { z } from "zod";
2
+
3
+ // Get login config path schema - simple command with no parameters
4
+ export const GetLoginConfigPathSchema = z
5
+ .object({})
6
+ .describe("Show the path to the login configuration file");
7
+
8
+ export type GetLoginConfigPathOptions = z.infer<
9
+ typeof GetLoginConfigPathSchema
10
+ >;
@@ -1,6 +1,6 @@
1
1
  export { loginPlugin } from "./login";
2
2
  export { logoutPlugin } from "./logout";
3
3
  export { mcpPlugin } from "./mcp";
4
- export { generateTypesPlugin } from "./generateTypes";
5
4
  export { bundleCodePlugin } from "./bundleCode";
6
- export { getConfigPathPlugin } from "./getConfigPath";
5
+ export { getLoginConfigPathPlugin } from "./getLoginConfigPath";
6
+ export { addPlugin } from "./add";
package/src/sdk.ts CHANGED
@@ -7,9 +7,9 @@ import {
7
7
  loginPlugin,
8
8
  logoutPlugin,
9
9
  mcpPlugin,
10
- generateTypesPlugin,
11
10
  bundleCodePlugin,
12
- getConfigPathPlugin,
11
+ getLoginConfigPathPlugin,
12
+ addPlugin,
13
13
  } from "./plugins/index";
14
14
 
15
15
  export interface ZapierCliSdkOptions {
@@ -29,9 +29,9 @@ export function createZapierCliSdk(
29
29
  });
30
30
 
31
31
  // Add CLI-specific plugins before registry
32
- sdk = sdk.addPlugin(generateTypesPlugin);
33
32
  sdk = sdk.addPlugin(bundleCodePlugin);
34
- sdk = sdk.addPlugin(getConfigPathPlugin);
33
+ sdk = sdk.addPlugin(getLoginConfigPathPlugin);
34
+ sdk = sdk.addPlugin(addPlugin);
35
35
  sdk = sdk.addPlugin(mcpPlugin);
36
36
  sdk = sdk.addPlugin(loginPlugin);
37
37
  sdk = sdk.addPlugin(logoutPlugin);
@@ -436,6 +436,9 @@ function addCommand(
436
436
  ): void {
437
437
  const command = program.command(commandName).description(config.description);
438
438
 
439
+ // Track whether we've already used a positional array parameter
440
+ let hasPositionalArray = false;
441
+
439
442
  // Add parameters to command
440
443
  config.parameters.forEach((param) => {
441
444
  // Convert camelCase to kebab-case for CLI display
@@ -447,6 +450,25 @@ function addCommand(
447
450
  `[${kebabName}]`,
448
451
  param.description || `${kebabName} parameter`,
449
452
  );
453
+ } else if (
454
+ param.required &&
455
+ param.type === "array" &&
456
+ !hasPositionalArray
457
+ ) {
458
+ // First required array parameter becomes a variadic positional argument
459
+ hasPositionalArray = true;
460
+ command.argument(
461
+ `<${kebabName}...>`,
462
+ param.description || `${kebabName} parameter`,
463
+ );
464
+ } else if (param.required && param.type === "array") {
465
+ // Subsequent required array parameters become required flags
466
+ const flags = [`--${kebabName}`];
467
+ const flagSignature = flags.join(", ") + ` <values...>`;
468
+ command.requiredOption(
469
+ flagSignature,
470
+ param.description || `${kebabName} parameter (required)`,
471
+ );
450
472
  } else if (param.required) {
451
473
  // Required parameters without resolvers become required positional arguments
452
474
  command.argument(
package/tsup.config.ts CHANGED
@@ -17,7 +17,7 @@ export default defineConfig({
17
17
  };
18
18
  },
19
19
  // Make the optional CLI login package external so it doesn't cause build issues
20
- external: ["@zapier/zapier-sdk-cli-login"],
20
+ external: ["@zapier/zapier-sdk-cli-login", "typescript"],
21
21
  // Use the build-specific tsconfig for tsup
22
22
  tsconfig: "tsconfig.build.json",
23
23
  });
@@ -1,21 +0,0 @@
1
- import type { Plugin, GetSdkType, ListActionsPluginProvides, ListInputFieldsPluginProvides, ManifestPluginProvides } from "@zapier/zapier-sdk";
2
- import { GenerateTypesSchema, type GenerateTypesOptions } from "./schemas";
3
- export interface GenerateTypesPluginProvides {
4
- generateTypes: (options: GenerateTypesOptions) => Promise<string>;
5
- context: {
6
- meta: {
7
- generateTypes: {
8
- inputSchema: typeof GenerateTypesSchema;
9
- };
10
- };
11
- };
12
- }
13
- export declare const generateTypesPlugin: Plugin<GetSdkType<ListActionsPluginProvides & ListInputFieldsPluginProvides & ManifestPluginProvides>, // requires these SDK methods
14
- {}, // requires no context
15
- GenerateTypesPluginProvides>;
16
- /**
17
- * Generate TypeScript types for a specific app (CLI version)
18
- */
19
- export declare function generateTypes(options: GenerateTypesOptions & {
20
- sdk: GetSdkType<ListActionsPluginProvides & ListInputFieldsPluginProvides & ManifestPluginProvides>;
21
- }): Promise<string>;