@vizzly-testing/cli 0.10.3 → 0.11.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 (47) hide show
  1. package/.claude-plugin/.mcp.json +8 -0
  2. package/.claude-plugin/README.md +114 -0
  3. package/.claude-plugin/commands/debug-diff.md +153 -0
  4. package/.claude-plugin/commands/setup.md +137 -0
  5. package/.claude-plugin/commands/suggest-screenshots.md +111 -0
  6. package/.claude-plugin/commands/tdd-status.md +43 -0
  7. package/.claude-plugin/marketplace.json +28 -0
  8. package/.claude-plugin/mcp/vizzly-server/cloud-api-provider.js +354 -0
  9. package/.claude-plugin/mcp/vizzly-server/index.js +861 -0
  10. package/.claude-plugin/mcp/vizzly-server/local-tdd-provider.js +422 -0
  11. package/.claude-plugin/mcp/vizzly-server/token-resolver.js +185 -0
  12. package/.claude-plugin/plugin.json +14 -0
  13. package/README.md +168 -8
  14. package/dist/cli.js +64 -0
  15. package/dist/client/index.js +13 -3
  16. package/dist/commands/login.js +195 -0
  17. package/dist/commands/logout.js +71 -0
  18. package/dist/commands/project.js +351 -0
  19. package/dist/commands/run.js +30 -0
  20. package/dist/commands/whoami.js +162 -0
  21. package/dist/plugin-loader.js +4 -2
  22. package/dist/sdk/index.js +16 -4
  23. package/dist/services/api-service.js +50 -7
  24. package/dist/services/auth-service.js +226 -0
  25. package/dist/types/client/index.d.ts +9 -3
  26. package/dist/types/commands/login.d.ts +11 -0
  27. package/dist/types/commands/logout.d.ts +11 -0
  28. package/dist/types/commands/project.d.ts +28 -0
  29. package/dist/types/commands/whoami.d.ts +11 -0
  30. package/dist/types/sdk/index.d.ts +9 -4
  31. package/dist/types/services/api-service.d.ts +2 -1
  32. package/dist/types/services/auth-service.d.ts +59 -0
  33. package/dist/types/utils/browser.d.ts +6 -0
  34. package/dist/types/utils/config-loader.d.ts +1 -1
  35. package/dist/types/utils/config-schema.d.ts +8 -174
  36. package/dist/types/utils/file-helpers.d.ts +18 -0
  37. package/dist/types/utils/global-config.d.ts +84 -0
  38. package/dist/utils/browser.js +44 -0
  39. package/dist/utils/config-loader.js +69 -3
  40. package/dist/utils/file-helpers.js +64 -0
  41. package/dist/utils/global-config.js +259 -0
  42. package/docs/api-reference.md +177 -6
  43. package/docs/authentication.md +334 -0
  44. package/docs/getting-started.md +21 -2
  45. package/docs/plugins.md +27 -0
  46. package/docs/test-integration.md +60 -10
  47. package/package.json +5 -3
@@ -8,23 +8,26 @@ import { VizzlyError, AuthError } from '../errors/vizzly-error.js';
8
8
  import crypto from 'crypto';
9
9
  import { getPackageVersion } from '../utils/package-info.js';
10
10
  import { getApiUrl, getApiToken, getUserAgent } from '../utils/environment-config.js';
11
+ import { getAuthTokens, saveAuthTokens } from '../utils/global-config.js';
11
12
 
12
13
  /**
13
14
  * ApiService class for direct API communication
14
15
  */
15
16
  export class ApiService {
16
17
  constructor(options = {}) {
17
- this.baseUrl = options.baseUrl || getApiUrl();
18
- this.token = options.token || getApiToken();
18
+ // Accept config as-is, no fallbacks to environment
19
+ // Config-loader handles all env/file resolution
20
+ this.baseUrl = options.apiUrl || options.baseUrl || getApiUrl();
21
+ this.token = options.apiKey || options.token || getApiToken(); // Accept both apiKey and token
19
22
  this.uploadAll = options.uploadAll || false;
20
23
 
21
24
  // Build User-Agent string
22
- const command = options.command || 'run'; // Default to 'run' for API service
25
+ const command = options.command || 'run';
23
26
  const baseUserAgent = `vizzly-cli/${getPackageVersion()} (${command})`;
24
27
  const sdkUserAgent = options.userAgent || getUserAgent();
25
28
  this.userAgent = sdkUserAgent ? `${baseUserAgent} ${sdkUserAgent}` : baseUserAgent;
26
29
  if (!this.token && !options.allowNoToken) {
27
- throw new VizzlyError('No API token provided. Set VIZZLY_TOKEN environment variable.');
30
+ throw new VizzlyError('No API token provided. Set VIZZLY_TOKEN environment variable or run "vizzly login".');
28
31
  }
29
32
  }
30
33
 
@@ -32,9 +35,10 @@ export class ApiService {
32
35
  * Make an API request
33
36
  * @param {string} endpoint - API endpoint
34
37
  * @param {Object} options - Fetch options
38
+ * @param {boolean} isRetry - Internal flag to prevent infinite retry loops
35
39
  * @returns {Promise<Object>} Response data
36
40
  */
37
- async request(endpoint, options = {}) {
41
+ async request(endpoint, options = {}, isRetry = false) {
38
42
  const url = `${this.baseUrl}${endpoint}`;
39
43
  const headers = {
40
44
  'User-Agent': this.userAgent,
@@ -59,9 +63,48 @@ export class ApiService {
59
63
  // ignore
60
64
  }
61
65
 
62
- // Handle authentication errors with user-friendly messages
66
+ // Handle authentication errors with automatic token refresh
67
+ if (response.status === 401 && !isRetry) {
68
+ // Attempt to refresh token if we have refresh token in global config
69
+ let auth = await getAuthTokens();
70
+ if (auth && auth.refreshToken) {
71
+ try {
72
+ // Attempt token refresh
73
+ let refreshResponse = await fetch(`${this.baseUrl}/api/auth/cli/refresh`, {
74
+ method: 'POST',
75
+ headers: {
76
+ 'Content-Type': 'application/json',
77
+ 'User-Agent': this.userAgent
78
+ },
79
+ body: JSON.stringify({
80
+ refreshToken: auth.refreshToken
81
+ })
82
+ });
83
+ if (refreshResponse.ok) {
84
+ let refreshData = await refreshResponse.json();
85
+
86
+ // Save new tokens to global config
87
+ await saveAuthTokens({
88
+ accessToken: refreshData.accessToken,
89
+ refreshToken: refreshData.refreshToken,
90
+ expiresAt: refreshData.expiresAt,
91
+ user: auth.user // Keep existing user data
92
+ });
93
+
94
+ // Update token for this service instance
95
+ this.token = refreshData.accessToken;
96
+
97
+ // Retry the original request with new token
98
+ return this.request(endpoint, options, true);
99
+ }
100
+ } catch {
101
+ // Token refresh failed, fall through to auth error
102
+ }
103
+ }
104
+ throw new AuthError('Invalid or expired API token. Please run "vizzly login" to authenticate.');
105
+ }
63
106
  if (response.status === 401) {
64
- throw new AuthError('Invalid or expired API token. Please check your VIZZLY_TOKEN environment variable and ensure it is valid.');
107
+ throw new AuthError('Invalid or expired API token. Please run "vizzly login" to authenticate.');
65
108
  }
66
109
  throw new VizzlyError(`API request failed: ${response.status}${errorText ? ` - ${errorText}` : ''} (URL: ${url})`);
67
110
  }
@@ -0,0 +1,226 @@
1
+ /**
2
+ * Authentication Service for Vizzly CLI
3
+ * Handles authentication flows with the Vizzly API
4
+ */
5
+
6
+ import { AuthError, VizzlyError } from '../errors/vizzly-error.js';
7
+ import { getApiUrl } from '../utils/environment-config.js';
8
+ import { getPackageVersion } from '../utils/package-info.js';
9
+ import { saveAuthTokens, clearAuthTokens, getAuthTokens } from '../utils/global-config.js';
10
+
11
+ /**
12
+ * AuthService class for CLI authentication
13
+ */
14
+ export class AuthService {
15
+ constructor(options = {}) {
16
+ this.baseUrl = options.baseUrl || getApiUrl();
17
+ this.userAgent = `vizzly-cli/${getPackageVersion()} (auth)`;
18
+ }
19
+
20
+ /**
21
+ * Make an unauthenticated API request
22
+ * @param {string} endpoint - API endpoint
23
+ * @param {Object} options - Fetch options
24
+ * @returns {Promise<Object>} Response data
25
+ */
26
+ async request(endpoint, options = {}) {
27
+ let url = `${this.baseUrl}${endpoint}`;
28
+ let headers = {
29
+ 'User-Agent': this.userAgent,
30
+ ...options.headers
31
+ };
32
+ let response = await fetch(url, {
33
+ ...options,
34
+ headers
35
+ });
36
+ if (!response.ok) {
37
+ let errorText = '';
38
+ let errorData = null;
39
+ try {
40
+ let contentType = response.headers.get('content-type');
41
+ if (contentType && contentType.includes('application/json')) {
42
+ errorData = await response.json();
43
+ errorText = errorData.error || errorData.message || '';
44
+ } else {
45
+ errorText = await response.text();
46
+ }
47
+ } catch {
48
+ errorText = response.statusText || '';
49
+ }
50
+ if (response.status === 401) {
51
+ throw new AuthError(errorText || 'Invalid credentials. Please check your email/username and password.');
52
+ }
53
+ if (response.status === 429) {
54
+ throw new VizzlyError('Too many login attempts. Please try again later.', 'RATE_LIMIT_ERROR');
55
+ }
56
+ throw new VizzlyError(`Authentication request failed: ${response.status}${errorText ? ` - ${errorText}` : ''}`, 'AUTH_REQUEST_ERROR');
57
+ }
58
+ return response.json();
59
+ }
60
+
61
+ /**
62
+ * Make an authenticated API request
63
+ * @param {string} endpoint - API endpoint
64
+ * @param {Object} options - Fetch options
65
+ * @returns {Promise<Object>} Response data
66
+ */
67
+ async authenticatedRequest(endpoint, options = {}) {
68
+ let auth = await getAuthTokens();
69
+ if (!auth || !auth.accessToken) {
70
+ throw new AuthError('No authentication token found. Please run "vizzly login" first.');
71
+ }
72
+ let url = `${this.baseUrl}${endpoint}`;
73
+ let headers = {
74
+ 'User-Agent': this.userAgent,
75
+ Authorization: `Bearer ${auth.accessToken}`,
76
+ ...options.headers
77
+ };
78
+ let response = await fetch(url, {
79
+ ...options,
80
+ headers
81
+ });
82
+ if (!response.ok) {
83
+ let errorText = '';
84
+ try {
85
+ let contentType = response.headers.get('content-type');
86
+ if (contentType && contentType.includes('application/json')) {
87
+ let errorData = await response.json();
88
+ errorText = errorData.error || errorData.message || '';
89
+ } else {
90
+ errorText = await response.text();
91
+ }
92
+ } catch {
93
+ errorText = response.statusText || '';
94
+ }
95
+ if (response.status === 401) {
96
+ throw new AuthError('Authentication token is invalid or expired. Please run "vizzly login" again.');
97
+ }
98
+ throw new VizzlyError(`API request failed: ${response.status}${errorText ? ` - ${errorText}` : ''}`, 'API_REQUEST_ERROR');
99
+ }
100
+ return response.json();
101
+ }
102
+
103
+ /**
104
+ * Initiate OAuth device flow
105
+ * @returns {Promise<Object>} Device code, user code, verification URL
106
+ */
107
+ async initiateDeviceFlow() {
108
+ return this.request('/api/auth/cli/device/initiate', {
109
+ method: 'POST',
110
+ headers: {
111
+ 'Content-Type': 'application/json'
112
+ }
113
+ });
114
+ }
115
+
116
+ /**
117
+ * Poll for device authorization
118
+ * @param {string} deviceCode - Device code from initiate
119
+ * @returns {Promise<Object>} Token data or pending status
120
+ */
121
+ async pollDeviceAuthorization(deviceCode) {
122
+ return this.request('/api/auth/cli/device/poll', {
123
+ method: 'POST',
124
+ headers: {
125
+ 'Content-Type': 'application/json'
126
+ },
127
+ body: JSON.stringify({
128
+ device_code: deviceCode
129
+ })
130
+ });
131
+ }
132
+
133
+ /**
134
+ * Complete device flow and save tokens
135
+ * @param {Object} tokenData - Token response from poll
136
+ * @returns {Promise<Object>} Token data with user info
137
+ */
138
+ async completeDeviceFlow(tokenData) {
139
+ // Save tokens to global config
140
+ await saveAuthTokens({
141
+ accessToken: tokenData.accessToken,
142
+ refreshToken: tokenData.refreshToken,
143
+ expiresAt: tokenData.expiresAt,
144
+ user: tokenData.user
145
+ });
146
+ return tokenData;
147
+ }
148
+
149
+ /**
150
+ * Refresh access token using refresh token
151
+ * @returns {Promise<Object>} New tokens
152
+ */
153
+ async refresh() {
154
+ let auth = await getAuthTokens();
155
+ if (!auth || !auth.refreshToken) {
156
+ throw new AuthError('No refresh token found. Please run "vizzly login" first.');
157
+ }
158
+ let response = await this.request('/api/auth/cli/refresh', {
159
+ method: 'POST',
160
+ headers: {
161
+ 'Content-Type': 'application/json'
162
+ },
163
+ body: JSON.stringify({
164
+ refreshToken: auth.refreshToken
165
+ })
166
+ });
167
+
168
+ // Update tokens in global config
169
+ await saveAuthTokens({
170
+ accessToken: response.accessToken,
171
+ refreshToken: response.refreshToken,
172
+ expiresAt: response.expiresAt,
173
+ user: auth.user // Keep existing user data
174
+ });
175
+ return response;
176
+ }
177
+
178
+ /**
179
+ * Logout and revoke tokens
180
+ * @returns {Promise<void>}
181
+ */
182
+ async logout() {
183
+ let auth = await getAuthTokens();
184
+ if (auth && auth.refreshToken) {
185
+ try {
186
+ // Attempt to revoke tokens on server
187
+ await this.request('/api/auth/cli/logout', {
188
+ method: 'POST',
189
+ headers: {
190
+ 'Content-Type': 'application/json'
191
+ },
192
+ body: JSON.stringify({
193
+ refreshToken: auth.refreshToken
194
+ })
195
+ });
196
+ } catch (error) {
197
+ // If server request fails, still clear local tokens
198
+ console.warn('Warning: Failed to revoke tokens on server:', error.message);
199
+ }
200
+ }
201
+
202
+ // Clear tokens from global config
203
+ await clearAuthTokens();
204
+ }
205
+
206
+ /**
207
+ * Get current user information
208
+ * @returns {Promise<Object>} User and organization data
209
+ */
210
+ async whoami() {
211
+ return this.authenticatedRequest('/api/auth/cli/whoami');
212
+ }
213
+
214
+ /**
215
+ * Check if user is authenticated
216
+ * @returns {Promise<boolean>} True if authenticated
217
+ */
218
+ async isAuthenticated() {
219
+ try {
220
+ await this.whoami();
221
+ return true;
222
+ } catch {
223
+ return false;
224
+ }
225
+ }
226
+ }
@@ -2,7 +2,7 @@
2
2
  * Take a screenshot for visual regression testing
3
3
  *
4
4
  * @param {string} name - Unique name for the screenshot
5
- * @param {Buffer} imageBuffer - PNG image data as a Buffer
5
+ * @param {Buffer|string} imageBuffer - PNG image data as a Buffer, or a file path to an image
6
6
  * @param {Object} [options] - Optional configuration
7
7
  * @param {Record<string, any>} [options.properties] - Additional properties to attach to the screenshot
8
8
  * @param {number} [options.threshold=0] - Pixel difference threshold (0-100)
@@ -11,13 +11,17 @@
11
11
  * @returns {Promise<void>}
12
12
  *
13
13
  * @example
14
- * // Basic usage
14
+ * // Basic usage with Buffer
15
15
  * import { vizzlyScreenshot } from '@vizzly-testing/cli/client';
16
16
  *
17
17
  * const screenshot = await page.screenshot();
18
18
  * await vizzlyScreenshot('homepage', screenshot);
19
19
  *
20
20
  * @example
21
+ * // Basic usage with file path
22
+ * await vizzlyScreenshot('homepage', './screenshots/homepage.png');
23
+ *
24
+ * @example
21
25
  * // With properties and threshold
22
26
  * await vizzlyScreenshot('checkout-form', screenshot, {
23
27
  * properties: {
@@ -28,8 +32,10 @@
28
32
  * });
29
33
  *
30
34
  * @throws {VizzlyError} When screenshot capture fails or client is not initialized
35
+ * @throws {VizzlyError} When file path is provided but file doesn't exist
36
+ * @throws {VizzlyError} When file cannot be read due to permissions or I/O errors
31
37
  */
32
- export function vizzlyScreenshot(name: string, imageBuffer: Buffer, options?: {
38
+ export function vizzlyScreenshot(name: string, imageBuffer: Buffer | string, options?: {
33
39
  properties?: Record<string, any>;
34
40
  threshold?: number;
35
41
  fullPage?: boolean;
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Login command implementation using OAuth device flow
3
+ * @param {Object} options - Command options
4
+ * @param {Object} globalOptions - Global CLI options
5
+ */
6
+ export function loginCommand(options?: any, globalOptions?: any): Promise<void>;
7
+ /**
8
+ * Validate login options
9
+ * @param {Object} options - Command options
10
+ */
11
+ export function validateLoginOptions(): any[];
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Logout command implementation
3
+ * @param {Object} options - Command options
4
+ * @param {Object} globalOptions - Global CLI options
5
+ */
6
+ export function logoutCommand(options?: any, globalOptions?: any): Promise<void>;
7
+ /**
8
+ * Validate logout options
9
+ * @param {Object} options - Command options
10
+ */
11
+ export function validateLogoutOptions(): any[];
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Project select command - configure project for current directory
3
+ * @param {Object} options - Command options
4
+ * @param {Object} globalOptions - Global CLI options
5
+ */
6
+ export function projectSelectCommand(options?: any, globalOptions?: any): Promise<void>;
7
+ /**
8
+ * Project list command - show all configured projects
9
+ * @param {Object} _options - Command options (unused)
10
+ * @param {Object} globalOptions - Global CLI options
11
+ */
12
+ export function projectListCommand(_options?: any, globalOptions?: any): Promise<void>;
13
+ /**
14
+ * Project token command - show/regenerate token for current directory
15
+ * @param {Object} _options - Command options (unused)
16
+ * @param {Object} globalOptions - Global CLI options
17
+ */
18
+ export function projectTokenCommand(_options?: any, globalOptions?: any): Promise<void>;
19
+ /**
20
+ * Project remove command - remove project configuration for current directory
21
+ * @param {Object} _options - Command options (unused)
22
+ * @param {Object} globalOptions - Global CLI options
23
+ */
24
+ export function projectRemoveCommand(_options?: any, globalOptions?: any): Promise<void>;
25
+ /**
26
+ * Validate project command options
27
+ */
28
+ export function validateProjectOptions(): any[];
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Whoami command implementation
3
+ * @param {Object} options - Command options
4
+ * @param {Object} globalOptions - Global CLI options
5
+ */
6
+ export function whoamiCommand(options?: any, globalOptions?: any): Promise<void>;
7
+ /**
8
+ * Validate whoami options
9
+ * @param {Object} options - Command options
10
+ */
11
+ export function validateWhoamiOptions(): any[];
@@ -81,11 +81,14 @@ export class VizzlySDK {
81
81
  /**
82
82
  * Capture a screenshot
83
83
  * @param {string} name - Screenshot name
84
- * @param {Buffer} imageBuffer - Image data
84
+ * @param {Buffer|string} imageBuffer - Image data as a Buffer, or a file path to an image
85
85
  * @param {import('../types').ScreenshotOptions} [options] - Options
86
86
  * @returns {Promise<void>}
87
+ * @throws {VizzlyError} When server is not running
88
+ * @throws {VizzlyError} When file path is provided but file doesn't exist
89
+ * @throws {VizzlyError} When file cannot be read due to permissions or I/O errors
87
90
  */
88
- screenshot(name: string, imageBuffer: Buffer, options?: any): Promise<void>;
91
+ screenshot(name: string, imageBuffer: Buffer | string, options?: any): Promise<void>;
89
92
  /**
90
93
  * Upload all captured screenshots
91
94
  * @param {import('../types').UploadOptions} [options] - Upload options
@@ -95,10 +98,12 @@ export class VizzlySDK {
95
98
  /**
96
99
  * Run local comparison in TDD mode
97
100
  * @param {string} name - Screenshot name
98
- * @param {Buffer} imageBuffer - Current image
101
+ * @param {Buffer|string} imageBuffer - Current image as a Buffer, or a file path to an image
99
102
  * @returns {Promise<import('../types').ComparisonResult>} Comparison result
103
+ * @throws {VizzlyError} When file path is provided but file doesn't exist
104
+ * @throws {VizzlyError} When file cannot be read due to permissions or I/O errors
100
105
  */
101
- compare(name: string, imageBuffer: Buffer): Promise<any>;
106
+ compare(name: string, imageBuffer: Buffer | string): Promise<any>;
102
107
  }
103
108
  export { loadConfig } from "../utils/config-loader.js";
104
109
  export { createLogger } from "../utils/logger.js";
@@ -11,9 +11,10 @@ export class ApiService {
11
11
  * Make an API request
12
12
  * @param {string} endpoint - API endpoint
13
13
  * @param {Object} options - Fetch options
14
+ * @param {boolean} isRetry - Internal flag to prevent infinite retry loops
14
15
  * @returns {Promise<Object>} Response data
15
16
  */
16
- request(endpoint: string, options?: any): Promise<any>;
17
+ request(endpoint: string, options?: any, isRetry?: boolean): Promise<any>;
17
18
  /**
18
19
  * Get build information
19
20
  * @param {string} buildId - Build ID
@@ -0,0 +1,59 @@
1
+ /**
2
+ * AuthService class for CLI authentication
3
+ */
4
+ export class AuthService {
5
+ constructor(options?: {});
6
+ baseUrl: any;
7
+ userAgent: string;
8
+ /**
9
+ * Make an unauthenticated API request
10
+ * @param {string} endpoint - API endpoint
11
+ * @param {Object} options - Fetch options
12
+ * @returns {Promise<Object>} Response data
13
+ */
14
+ request(endpoint: string, options?: any): Promise<any>;
15
+ /**
16
+ * Make an authenticated API request
17
+ * @param {string} endpoint - API endpoint
18
+ * @param {Object} options - Fetch options
19
+ * @returns {Promise<Object>} Response data
20
+ */
21
+ authenticatedRequest(endpoint: string, options?: any): Promise<any>;
22
+ /**
23
+ * Initiate OAuth device flow
24
+ * @returns {Promise<Object>} Device code, user code, verification URL
25
+ */
26
+ initiateDeviceFlow(): Promise<any>;
27
+ /**
28
+ * Poll for device authorization
29
+ * @param {string} deviceCode - Device code from initiate
30
+ * @returns {Promise<Object>} Token data or pending status
31
+ */
32
+ pollDeviceAuthorization(deviceCode: string): Promise<any>;
33
+ /**
34
+ * Complete device flow and save tokens
35
+ * @param {Object} tokenData - Token response from poll
36
+ * @returns {Promise<Object>} Token data with user info
37
+ */
38
+ completeDeviceFlow(tokenData: any): Promise<any>;
39
+ /**
40
+ * Refresh access token using refresh token
41
+ * @returns {Promise<Object>} New tokens
42
+ */
43
+ refresh(): Promise<any>;
44
+ /**
45
+ * Logout and revoke tokens
46
+ * @returns {Promise<void>}
47
+ */
48
+ logout(): Promise<void>;
49
+ /**
50
+ * Get current user information
51
+ * @returns {Promise<Object>} User and organization data
52
+ */
53
+ whoami(): Promise<any>;
54
+ /**
55
+ * Check if user is authenticated
56
+ * @returns {Promise<boolean>} True if authenticated
57
+ */
58
+ isAuthenticated(): Promise<boolean>;
59
+ }
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Open a URL in the default browser
3
+ * @param {string} url - URL to open
4
+ * @returns {Promise<boolean>} True if successful
5
+ */
6
+ export function openBrowser(url: string): Promise<boolean>;
@@ -19,7 +19,7 @@ export function loadConfig(configPath?: any, cliOverrides?: {}): Promise<{
19
19
  openReport: boolean;
20
20
  };
21
21
  plugins: any[];
22
- apiKey: string;
22
+ apiKey: any;
23
23
  apiUrl: string;
24
24
  }>;
25
25
  export function getScreenshotPaths(config: any): any[];