@zapier/zapier-sdk-cli 0.13.3 → 0.13.4

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zapier/zapier-sdk-cli",
3
- "version": "0.13.3",
3
+ "version": "0.13.4",
4
4
  "description": "Command line interface for Zapier SDK",
5
5
  "main": "dist/index.cjs",
6
6
  "module": "dist/index.mjs",
@@ -42,9 +42,9 @@
42
42
  "ora": "^8.2.0",
43
43
  "pkce-challenge": "^5.0.0",
44
44
  "zod": "^3.25.67",
45
- "@zapier/zapier-sdk": "0.13.3",
46
- "@zapier/zapier-sdk-cli-login": "0.3.2",
47
- "@zapier/zapier-sdk-mcp": "0.3.16"
45
+ "@zapier/zapier-sdk": "0.13.4",
46
+ "@zapier/zapier-sdk-mcp": "0.3.17",
47
+ "@zapier/zapier-sdk-cli-login": "0.3.2"
48
48
  },
49
49
  "devDependencies": {
50
50
  "@types/express": "^5.0.3",
package/src/index.ts CHANGED
@@ -1,4 +1,12 @@
1
1
  // Main exports for the CLI package
2
2
  export { createZapierCliSdk, type ZapierCliSdkOptions } from "./sdk";
3
3
 
4
+ // Export CLI telemetry builders and types
5
+ export type {
6
+ CliEventContext,
7
+ CliCommandExecutedEventData,
8
+ } from "./telemetry/builders";
9
+ export { buildCliCommandExecutedEvent } from "./telemetry/builders";
10
+ export type { CliCommandExecutedEvent } from "./telemetry/events";
11
+
4
12
  // All CLI functionality is now schema-driven via generateCLICommands
@@ -1,8 +1,18 @@
1
1
  import { createFunction } from "@zapier/zapier-sdk";
2
2
  import type { Plugin } from "@zapier/zapier-sdk";
3
+ import type { EventEmissionContext } from "@zapier/zapier-sdk";
3
4
  import login from "../../utils/auth/login";
4
5
  import { getLoggedInUser } from "@zapier/zapier-sdk-cli-login";
5
6
  import { LoginSchema, type LoginOptions } from "./schemas";
7
+ import { buildCliCommandExecutedEvent } from "../../telemetry/builders";
8
+ import cliPackageJson from "../../../package.json";
9
+
10
+ interface CliContext {
11
+ session_id?: string | null;
12
+ selected_api?: string | null;
13
+ app_id?: number | null;
14
+ app_version_id?: number | null;
15
+ }
6
16
 
7
17
  interface LoginPluginProvides {
8
18
  login: (options: LoginOptions) => Promise<void>;
@@ -16,6 +26,9 @@ interface LoginPluginProvides {
16
26
  };
17
27
  }
18
28
 
29
+ const CLI_COMMAND_EXECUTED_EVENT_SUBJECT =
30
+ "platform.sdk.CliCommandExecutedEvent";
31
+
19
32
  const loginWithSdk = createFunction(async function loginWithSdk(
20
33
  options: LoginOptions,
21
34
  ): Promise<void> {
@@ -33,16 +46,63 @@ const loginWithSdk = createFunction(async function loginWithSdk(
33
46
  setTimeout(() => process.exit(0), 100);
34
47
  }, LoginSchema);
35
48
 
36
- export const loginPlugin: Plugin<{}, {}, LoginPluginProvides> = () => ({
37
- login: loginWithSdk,
38
- context: {
39
- meta: {
40
- login: {
41
- categories: ["account"],
42
- inputSchema: LoginSchema,
49
+ export const loginPlugin: Plugin<
50
+ {},
51
+ EventEmissionContext & CliContext,
52
+ LoginPluginProvides
53
+ > = ({ context }) => {
54
+ // Wrap the login function to emit telemetry events
55
+ const loginWithTelemetry = async (options: LoginOptions): Promise<void> => {
56
+ const startTime = Date.now();
57
+ let success = false;
58
+ let errorMessage: string | null = null;
59
+
60
+ try {
61
+ await loginWithSdk(options);
62
+ success = true;
63
+ } catch (error) {
64
+ success = false;
65
+ errorMessage = error instanceof Error ? error.message : "Login failed";
66
+ throw error;
67
+ } finally {
68
+ // Emit CLI command executed event if event emission is available
69
+ const event = buildCliCommandExecutedEvent({
70
+ data: {
71
+ cli_primary_command: "login",
72
+ success_flag: success,
73
+ execution_duration_ms: Date.now() - startTime,
74
+ exit_code: success ? 0 : 1,
75
+ error_message: errorMessage,
76
+ command_category: "authentication",
77
+ requires_auth: false,
78
+ cli_arguments: [
79
+ "login",
80
+ options.timeout ? `--timeout=${options.timeout}` : null,
81
+ ].filter(Boolean) as string[],
82
+ },
83
+ context: {
84
+ session_id: context.session_id,
85
+ selected_api: context.selected_api,
86
+ app_id: context.app_id,
87
+ app_version_id: context.app_version_id,
88
+ },
89
+ cliVersion: cliPackageJson.version,
90
+ });
91
+ context.eventEmission.emit(CLI_COMMAND_EXECUTED_EVENT_SUBJECT, event);
92
+ }
93
+ };
94
+
95
+ return {
96
+ login: loginWithTelemetry,
97
+ context: {
98
+ meta: {
99
+ login: {
100
+ categories: ["account"],
101
+ inputSchema: LoginSchema,
102
+ },
43
103
  },
44
104
  },
45
- },
46
- });
105
+ };
106
+ };
47
107
 
48
108
  export type { LoginPluginProvides };
package/src/sdk.ts CHANGED
@@ -14,6 +14,14 @@ import {
14
14
 
15
15
  export interface ZapierCliSdkOptions {
16
16
  debug?: boolean;
17
+ eventEmission?: {
18
+ enabled?: boolean;
19
+ transport?: {
20
+ type: "http" | "console" | "noop";
21
+ endpoint?: string;
22
+ headers?: Record<string, string>;
23
+ };
24
+ };
17
25
  }
18
26
 
19
27
  /**
@@ -26,6 +34,7 @@ export function createZapierCliSdk(
26
34
  // Create SDK instance without registry
27
35
  let sdk = createZapierSdkWithoutRegistry({
28
36
  debug: options.debug,
37
+ eventEmission: options.eventEmission,
29
38
  });
30
39
 
31
40
  // Add CLI-specific plugins before registry
@@ -0,0 +1,114 @@
1
+ /**
2
+ * CLI-specific event builders
3
+ *
4
+ * Provides builder functions for CLI command telemetry that auto-populate
5
+ * common CLI fields and system information.
6
+ */
7
+
8
+ import type { BaseEvent } from "@zapier/zapier-sdk";
9
+ import {
10
+ generateEventId,
11
+ getCurrentTimestamp,
12
+ getReleaseId,
13
+ getOsInfo,
14
+ getPlatformVersions,
15
+ isCi,
16
+ getCiPlatform,
17
+ } from "@zapier/zapier-sdk";
18
+ import type { CliCommandExecutedEvent } from "./events";
19
+ import cliPackageJson from "../../package.json";
20
+
21
+ // CLI context that can be injected into events
22
+ export interface CliEventContext {
23
+ customuser_id?: number | null;
24
+ account_id?: number | null;
25
+ identity_id?: number | null;
26
+ visitor_id?: string | null;
27
+ correlation_id?: string | null;
28
+ session_id?: string | null;
29
+ selected_api?: string | null;
30
+ app_id?: number | null;
31
+ app_version_id?: number | null;
32
+ }
33
+
34
+ // Create base event for CLI events
35
+ function createCliBaseEvent(context: CliEventContext = {}): BaseEvent {
36
+ return {
37
+ event_id: generateEventId(),
38
+ timestamp_ms: getCurrentTimestamp(),
39
+ release_id: getReleaseId(),
40
+ customuser_id: context.customuser_id ?? null,
41
+ account_id: context.account_id ?? null,
42
+ identity_id: context.identity_id ?? null,
43
+ visitor_id: context.visitor_id ?? null,
44
+ correlation_id: context.correlation_id ?? null,
45
+ };
46
+ }
47
+
48
+ // CLI Command Executed Event Builder
49
+ // This interface corresponds to the cli_command_executed_event.avsc Avro schema
50
+ export interface CliCommandExecutedEventData {
51
+ cli_primary_command: string;
52
+ success_flag: boolean;
53
+ execution_duration_ms?: number | null;
54
+ exit_code?: number | null;
55
+ error_message?: string | null;
56
+ command_category?: string | null;
57
+ requires_auth?: boolean | null;
58
+ cli_arguments?: (string | null)[] | null;
59
+ // Allow overriding auto-detected values if needed
60
+ cli_version?: string | null;
61
+ made_network_requests?: boolean | null;
62
+ files_modified_count?: number | null;
63
+ files_created_count?: number | null;
64
+ files_processed_size_bytes?: number | null;
65
+ peak_memory_usage_bytes?: number | null;
66
+ cpu_time_ms?: number | null;
67
+ subprocess_count?: number | null;
68
+ package_manager?: string | null;
69
+ }
70
+
71
+ export function buildCliCommandExecutedEvent({
72
+ data,
73
+ context = {},
74
+ cliVersion = cliPackageJson.version,
75
+ }: {
76
+ data: CliCommandExecutedEventData;
77
+ context?: CliEventContext;
78
+ cliVersion?: string;
79
+ }): CliCommandExecutedEvent {
80
+ const osInfo = getOsInfo();
81
+ const platformVersions = getPlatformVersions();
82
+
83
+ return {
84
+ ...createCliBaseEvent(context),
85
+ system_name: "zapier-sdk-cli",
86
+ session_id: context.session_id ?? null,
87
+ cli_version: data.cli_version ?? cliVersion,
88
+ cli_arguments: data.cli_arguments ?? null,
89
+ cli_primary_command: data.cli_primary_command,
90
+ os_platform: osInfo.platform,
91
+ os_release: osInfo.release,
92
+ os_architecture: osInfo.architecture,
93
+ platform_versions: platformVersions,
94
+ selected_api: context.selected_api ?? null,
95
+ app_id: context.app_id ?? null,
96
+ app_version_id: context.app_version_id ?? null,
97
+ execution_duration_ms: data.execution_duration_ms ?? null,
98
+ success_flag: data.success_flag,
99
+ exit_code: data.exit_code ?? (data.success_flag ? 0 : 1),
100
+ error_message: data.error_message ?? null,
101
+ command_category: data.command_category ?? null,
102
+ requires_auth: data.requires_auth ?? null,
103
+ is_ci_environment: isCi(),
104
+ ci_platform: getCiPlatform(),
105
+ package_manager: data.package_manager ?? "pnpm", // Default based on project setup
106
+ made_network_requests: data.made_network_requests ?? null,
107
+ files_modified_count: data.files_modified_count ?? null,
108
+ files_created_count: data.files_created_count ?? null,
109
+ files_processed_size_bytes: data.files_processed_size_bytes ?? null,
110
+ peak_memory_usage_bytes: data.peak_memory_usage_bytes ?? null,
111
+ cpu_time_ms: data.cpu_time_ms ?? null,
112
+ subprocess_count: data.subprocess_count ?? null,
113
+ };
114
+ }
@@ -0,0 +1,39 @@
1
+ /**
2
+ * CLI-specific telemetry event definitions
3
+ */
4
+
5
+ import type { BaseEvent } from "@zapier/zapier-sdk";
6
+
7
+ /**
8
+ * Event emitted when a CLI command is executed
9
+ */
10
+ export interface CliCommandExecutedEvent extends BaseEvent {
11
+ system_name: string;
12
+ session_id?: string | null;
13
+ cli_version?: string | null;
14
+ cli_arguments?: (string | null)[] | null;
15
+ cli_primary_command?: string | null;
16
+ os_platform?: string | null;
17
+ os_release?: string | null;
18
+ os_architecture?: string | null;
19
+ platform_versions?: Record<string, string | null> | null;
20
+ selected_api?: string | null;
21
+ app_id?: number | null;
22
+ app_version_id?: number | null;
23
+ execution_duration_ms?: number | null;
24
+ success_flag: boolean;
25
+ exit_code?: number | null;
26
+ error_message?: string | null;
27
+ command_category?: string | null;
28
+ requires_auth?: boolean | null;
29
+ is_ci_environment?: boolean | null;
30
+ ci_platform?: string | null;
31
+ package_manager?: string | null;
32
+ made_network_requests?: boolean | null;
33
+ files_modified_count?: number | null;
34
+ files_created_count?: number | null;
35
+ files_processed_size_bytes?: number | null;
36
+ peak_memory_usage_bytes?: number | null;
37
+ cpu_time_ms?: number | null;
38
+ subprocess_count?: number | null;
39
+ }
@@ -1,5 +1,9 @@
1
1
  import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
2
2
  import chalk from "chalk";
3
+ import { generateCliCommands } from "./cli-generator";
4
+ import { Command } from "commander";
5
+ import type { ZapierSdk } from "@zapier/zapier-sdk";
6
+ import { z } from "zod";
3
7
 
4
8
  // We need to create a test for the formatItemsGeneric function that's defined in cli-generator.ts
5
9
  // Since it's not exported, we'll create a simple test that verifies the numbering logic
@@ -91,3 +95,101 @@ describe("CLI Generator Pagination Numbering", () => {
91
95
  expect(output.some((line) => line.includes("2."))).toBe(true);
92
96
  });
93
97
  });
98
+
99
+ // Create proper Zod schema for testing
100
+ const testSchema = z
101
+ .object({
102
+ maxItems: z.number().optional(),
103
+ pageSize: z.number().optional(),
104
+ })
105
+ .describe("List all apps");
106
+
107
+ // Mock SDK for CLI tests
108
+ const mockSdk = {
109
+ getRegistry: vi.fn(() => ({
110
+ functions: [
111
+ {
112
+ name: "listApps",
113
+ type: "list",
114
+ inputSchema: testSchema,
115
+ },
116
+ ],
117
+ categories: [],
118
+ })),
119
+ listApps: vi.fn().mockResolvedValue({ data: [] }),
120
+ } as unknown as ZapierSdk;
121
+
122
+ describe("CLI Command Generation", () => {
123
+ beforeEach(() => {
124
+ vi.clearAllMocks();
125
+ });
126
+
127
+ it("should execute CLI commands successfully", async () => {
128
+ const program = new Command();
129
+ program.exitOverride(); // Prevent process.exit() in tests
130
+
131
+ // Generate CLI commands
132
+ generateCliCommands(program, mockSdk);
133
+
134
+ // Execute the command through Commander.js parsing
135
+ await program.parseAsync(["node", "test", "list-apps", "--json"]);
136
+
137
+ // Verify the SDK method was called
138
+ expect(mockSdk.listApps).toHaveBeenCalled();
139
+ });
140
+
141
+ it("should handle CLI command failures gracefully", async () => {
142
+ const program = new Command();
143
+ program.exitOverride(); // Prevent process.exit() in tests
144
+ const consoleSpy = vi.spyOn(console, "error").mockImplementation(() => {});
145
+
146
+ // Generate CLI commands
147
+ generateCliCommands(program, mockSdk);
148
+
149
+ // Mock the SDK method to throw an error
150
+ (mockSdk as unknown).listApps = vi
151
+ .fn()
152
+ .mockRejectedValue(new Error("API error"));
153
+
154
+ try {
155
+ // Execute the command through Commander.js parsing
156
+ await program.parseAsync(["node", "test", "list-apps", "--json"]);
157
+ } catch {
158
+ // Expected due to exitOverride throwing CommanderError
159
+ }
160
+
161
+ // Verify the SDK method was attempted
162
+ expect(mockSdk.listApps).toHaveBeenCalled();
163
+
164
+ // Cleanup
165
+ consoleSpy.mockRestore();
166
+ });
167
+
168
+ it("should generate commands for SDK without event emission", async () => {
169
+ const mockSdkWithoutEvents = {
170
+ getRegistry: vi.fn(() => ({
171
+ functions: [
172
+ {
173
+ name: "listApps",
174
+ type: "list",
175
+ inputSchema: testSchema,
176
+ },
177
+ ],
178
+ categories: [],
179
+ })),
180
+ listApps: vi.fn().mockResolvedValue({ data: [] }),
181
+ } as unknown as ZapierSdk;
182
+
183
+ const program = new Command();
184
+ program.exitOverride(); // Prevent process.exit() in tests
185
+
186
+ // Generate CLI commands
187
+ generateCliCommands(program, mockSdkWithoutEvents);
188
+
189
+ // Should not throw even without event emission capability
190
+ await program.parseAsync(["node", "test", "list-apps", "--json"]);
191
+
192
+ // Verify the command executed successfully
193
+ expect(mockSdkWithoutEvents.listApps).toHaveBeenCalled();
194
+ });
195
+ });