@vizzly-testing/cli 0.29.6 → 0.31.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 (87) hide show
  1. package/README.md +34 -6
  2. package/dist/api/client.js +6 -3
  3. package/dist/api/endpoints.js +59 -0
  4. package/dist/api/index.js +1 -1
  5. package/dist/auth/core.js +1 -1
  6. package/dist/auth/index.js +2 -1
  7. package/dist/cli.js +206 -51
  8. package/dist/client/index.js +21 -29
  9. package/dist/commands/api.js +145 -90
  10. package/dist/commands/baselines.js +78 -47
  11. package/dist/commands/builds.js +9 -8
  12. package/dist/commands/comparisons.js +7 -8
  13. package/dist/commands/config-cmd.js +15 -7
  14. package/dist/commands/context.js +870 -0
  15. package/dist/commands/doctor.js +116 -81
  16. package/dist/commands/init.js +202 -201
  17. package/dist/commands/login.js +134 -65
  18. package/dist/commands/logout.js +18 -7
  19. package/dist/commands/preview.js +29 -57
  20. package/dist/commands/project.js +120 -0
  21. package/dist/commands/projects.js +9 -2
  22. package/dist/commands/review.js +236 -202
  23. package/dist/commands/run.js +75 -72
  24. package/dist/commands/status.js +228 -150
  25. package/dist/commands/tdd-daemon-process.js +98 -0
  26. package/dist/commands/tdd-daemon.js +318 -212
  27. package/dist/commands/tdd.js +23 -8
  28. package/dist/commands/upload.js +10 -9
  29. package/dist/commands/whoami.js +64 -26
  30. package/dist/config/core.js +37 -36
  31. package/dist/context/local-workspace-provider.js +532 -0
  32. package/dist/context/provider-resolver.js +36 -0
  33. package/dist/index.js +5 -4
  34. package/dist/plugin-loader.js +55 -49
  35. package/dist/project/core.js +2 -2
  36. package/dist/project/operations.js +6 -3
  37. package/dist/reporter/reporter-bundle.css +2 -1
  38. package/dist/reporter/reporter-bundle.iife.js +119 -119
  39. package/dist/reporter-ssr/ssr-entry.js +605 -509
  40. package/dist/sdk/index.js +162 -107
  41. package/dist/server/handlers/tdd-handler.js +30 -54
  42. package/dist/server/routers/assets.js +4 -0
  43. package/dist/server/routers/baseline.js +195 -178
  44. package/dist/server/routers/events.js +226 -148
  45. package/dist/server/routers/projects.js +14 -5
  46. package/dist/server/routers/screenshot.js +1 -31
  47. package/dist/services/build-manager.js +19 -97
  48. package/dist/services/config-service.js +26 -34
  49. package/dist/services/index.js +2 -4
  50. package/dist/services/static-report-generator.js +11 -12
  51. package/dist/services/test-runner.js +21 -27
  52. package/dist/tdd/core/hotspot-coverage.js +1 -1
  53. package/dist/tdd/index.js +1 -1
  54. package/dist/tdd/metadata/region-metadata.js +0 -37
  55. package/dist/tdd/runtime-context.js +78 -0
  56. package/dist/tdd/server-registry.js +38 -17
  57. package/dist/tdd/services/baseline-manager.js +23 -0
  58. package/dist/tdd/services/hotspot-service.js +1 -14
  59. package/dist/tdd/tdd-service.js +82 -113
  60. package/dist/types/client.d.ts +36 -1
  61. package/dist/types/index.d.ts +114 -22
  62. package/dist/types/sdk.d.ts +47 -7
  63. package/dist/uploader/core.js +5 -4
  64. package/dist/uploader/index.js +79 -2
  65. package/dist/uploader/operations.js +40 -5
  66. package/dist/utils/api-url.js +14 -0
  67. package/dist/utils/async-utils.js +26 -0
  68. package/dist/utils/ci-env.js +19 -12
  69. package/dist/utils/colors.js +46 -44
  70. package/dist/utils/config-loader.js +39 -65
  71. package/dist/utils/config-schema.js +19 -55
  72. package/dist/utils/fetch-utils.js +12 -5
  73. package/dist/utils/git.js +35 -72
  74. package/dist/utils/global-config.js +22 -3
  75. package/dist/utils/output.js +19 -7
  76. package/dist/utils/patterns.js +14 -0
  77. package/dist/utils/project-link-store.js +105 -0
  78. package/dist/utils/report-data.js +31 -0
  79. package/dist/utils/screenshot-options.js +28 -0
  80. package/dist/utils/secret-store.js +65 -0
  81. package/dist/utils/security.js +3 -2
  82. package/package.json +14 -11
  83. package/dist/services/screenshot-server.js +0 -65
  84. package/dist/services/server-manager.js +0 -81
  85. package/dist/services/uploader.js +0 -100
  86. package/dist/tdd/services/region-service.js +0 -75
  87. package/dist/utils/build-history.js +0 -103
package/README.md CHANGED
@@ -1,23 +1,23 @@
1
1
  # Vizzly CLI
2
2
 
3
- > Visual proof that your UI works
3
+ > Visual review for agents and teams
4
4
 
5
5
  [![npm version](https://img.shields.io/npm/v/@vizzly-testing/cli.svg)](https://www.npmjs.com/package/@vizzly-testing/cli)
6
6
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
7
7
 
8
- Visual bugs slip through code review. They hide in pixel-perfect mockups, sneak past unit tests, and show up right when you're about to ship. Vizzly catches them first.
8
+ Vizzly keeps the visual truth behind your UI: approved baselines, meaningful diffs, review state, comments, and preview links in one place. That makes it useful for humans reviewing product changes and for LLM agents that need to understand what the UI is supposed to look like before they edit it.
9
9
 
10
- Unlike tools that re-render components in isolation, Vizzly captures screenshots directly from your functional tests—the *real thing*. Whether you're validating AI-generated code or testing manual changes, you get visual proof before anything hits production.
10
+ Unlike tools that re-render components in isolation, Vizzly captures screenshots directly from your functional tests: the real thing. Whether you're validating AI-generated code or testing manual changes, you get reviewed visual evidence before anything hits production.
11
11
 
12
12
  ## Why Vizzly?
13
13
 
14
- **Local TDD workflow.** See changes as you type, not after CI. The `vizzly tdd` command runs a local dashboard that compares screenshots instantly—no cloud roundtrip, no waiting.
14
+ **Local TDD workflow.** See changes as you type, not after CI. The `vizzly tdd` command runs a local dashboard that compares screenshots instantly and exposes the current workspace as local context.
15
15
 
16
- **Smart diffing with Honeydiff.** Our Rust-based comparison engine is 12x faster than alternatives and ignores the noise: timestamps, ads, font rendering differences. It finds real changes.
16
+ **Meaningful diff metadata.** Vizzly stores rich diff evidence: changed regions, cluster metadata, fingerprints, hotspots, confirmed regions, and image URLs. Agents can inspect what changed instead of guessing from a pass/fail label.
17
17
 
18
18
  **Any screenshot source.** Playwright, Cypress, Puppeteer, Selenium, native mobile apps, or even design mockups. If you can capture it, Vizzly can compare it.
19
19
 
20
- **Team-based pricing.** Pay for your team, not your screenshots. Test everything without budget anxiety.
20
+ **Approved baselines as truth.** Cloud context carries human review state. Local context carries the downloaded or generated baseline metadata. That is the bridge between TDD locally and collaborative review in Vizzly.
21
21
 
22
22
  ## Quick Start
23
23
 
@@ -58,6 +58,33 @@ export VIZZLY_TOKEN=your-project-token
58
58
  vizzly run "npm test" --wait
59
59
  ```
60
60
 
61
+ ### Visual Context For Agents
62
+
63
+ Use `vizzly context` when you want Vizzly to act like a visual context store, not just a test runner.
64
+
65
+ This is especially useful for LLM agents, automation, and quick debugging loops. Instead of
66
+ making a bunch of narrow API calls, you can ask for one build, comparison, screenshot, or review
67
+ queue bundle and get the evidence in one place.
68
+
69
+ ```bash
70
+ # Cloud context for a build or comparison
71
+ vizzly context build abc123
72
+ vizzly context comparison def456 --json
73
+
74
+ # Local workspace context from .vizzly/
75
+ vizzly context build current --source local
76
+ vizzly context build current --source local --agent
77
+ vizzly context screenshot build-detail-screenshots --source local --json
78
+ ```
79
+
80
+ `--json` is the durable automation path. `--agent` gives a compact handoff for prompt assembly and local dogfooding. Add `--full` when an agent really needs the complete build context payload, or `--include screenshots,diffs,comments` when the compact JSON needs selected detail.
81
+
82
+ Local context is read-only and file-backed. It reads your existing `.vizzly` workspace state from
83
+ TDD runs, including screenshots, diffs, and any saved hotspot or region metadata.
84
+
85
+ Cloud context is also read-only right now. That is intentional. It keeps the trust model simple:
86
+ Vizzly helps you see and inspect visual changes, while people still decide what gets approved.
87
+
61
88
  ## Capture Screenshots
62
89
 
63
90
  Add screenshots to your existing tests:
@@ -116,6 +143,7 @@ export default {
116
143
  | `vizzly tdd start` | Start local TDD server with dashboard |
117
144
  | `vizzly tdd run "cmd"` | Run tests once, generate static report |
118
145
  | `vizzly run "cmd"` | Run tests with cloud integration |
146
+ | `vizzly context ...` | Fetch visual context bundles for builds, comparisons, and screenshots |
119
147
  | `vizzly upload <dir>` | Upload existing screenshots |
120
148
  | `vizzly login` | Authenticate via browser |
121
149
  | `vizzly doctor` | Validate local setup |
@@ -14,6 +14,9 @@ import { buildApiUrl, buildRequestHeaders, buildUserAgent, extractErrorBody, isA
14
14
  * Default API URL
15
15
  */
16
16
  export const DEFAULT_API_URL = 'https://app.vizzly.dev';
17
+ function isProjectToken(token) {
18
+ return typeof token === 'string' && token.startsWith('vzt_');
19
+ }
17
20
 
18
21
  /**
19
22
  * Create an API client with the given configuration
@@ -63,7 +66,7 @@ export function createApiClient(options = {}) {
63
66
  let errorBody = await extractErrorBody(response);
64
67
 
65
68
  // Handle 401 with token refresh
66
- if (shouldRetryWithRefresh(response.status, isRetry, await hasRefreshToken())) {
69
+ if (shouldRetryWithRefresh(response.status, isRetry, !isProjectToken(token) && (await hasRefreshToken()))) {
67
70
  let refreshed = await attemptTokenRefresh();
68
71
  if (refreshed) {
69
72
  token = refreshed;
@@ -73,7 +76,7 @@ export function createApiClient(options = {}) {
73
76
 
74
77
  // Auth error
75
78
  if (isAuthError(response.status)) {
76
- throw new AuthError('Invalid or expired API token. Link a project via "vizzly project:select" or set VIZZLY_TOKEN.');
79
+ throw new AuthError('Invalid or expired API token. Run "vizzly project link <org>/<project>" or set VIZZLY_TOKEN.');
77
80
  }
78
81
 
79
82
  // Other errors
@@ -119,7 +122,7 @@ export function createApiClient(options = {}) {
119
122
  await saveAuthTokens({
120
123
  accessToken: data.accessToken,
121
124
  refreshToken: data.refreshToken,
122
- expiresAt: data.expiresAt,
125
+ expiresAt: data.expiresAt || new Date(Date.now() + data.expiresIn * 1000).toISOString(),
123
126
  user: auth.user
124
127
  });
125
128
  return data.accessToken;
@@ -210,6 +210,65 @@ export async function getComparison(client, comparisonId) {
210
210
  return response.comparison;
211
211
  }
212
212
 
213
+ /**
214
+ * Get build context bundle for agent and reviewer workflows
215
+ * @param {Object} client - API client
216
+ * @param {string} buildId - Build ID
217
+ * @returns {Promise<Object>} Build context bundle
218
+ */
219
+ export async function getBuildContext(client, buildId) {
220
+ return client.request(`/api/sdk/context/builds/${buildId}`);
221
+ }
222
+
223
+ /**
224
+ * Get comparison context bundle with similarity and history
225
+ * @param {Object} client - API client
226
+ * @param {string} comparisonId - Comparison ID
227
+ * @param {Object} options - Optional query params
228
+ * @returns {Promise<Object>} Comparison context bundle
229
+ */
230
+ export async function getComparisonContext(client, comparisonId, options = {}) {
231
+ let endpoint = buildEndpointWithParams(`/api/sdk/context/comparisons/${comparisonId}`, options);
232
+ return client.request(endpoint);
233
+ }
234
+
235
+ /**
236
+ * Get screenshot context bundle for a screenshot name
237
+ * @param {Object} client - API client
238
+ * @param {string} screenshotName - Screenshot name
239
+ * @param {Object} options - Optional query params
240
+ * @returns {Promise<Object>} Screenshot context bundle
241
+ */
242
+ export async function getScreenshotContext(client, screenshotName, options = {}) {
243
+ let encodedName = encodeURIComponent(screenshotName);
244
+ let endpoint = buildEndpointWithParams(`/api/sdk/context/screenshots/${encodedName}`, options);
245
+ return client.request(endpoint);
246
+ }
247
+
248
+ /**
249
+ * Get project-scoped similar comparisons for a fingerprint hash
250
+ * @param {Object} client - API client
251
+ * @param {string} fingerprintHash - Honeydiff fingerprint hash
252
+ * @param {Object} options - Optional query params
253
+ * @returns {Promise<Object>} Fingerprint context bundle
254
+ */
255
+ export async function getSimilarFingerprintContext(client, fingerprintHash, options = {}) {
256
+ let encodedFingerprint = encodeURIComponent(fingerprintHash);
257
+ let endpoint = buildEndpointWithParams(`/api/sdk/context/fingerprints/${encodedFingerprint}/similar`, options);
258
+ return client.request(endpoint);
259
+ }
260
+
261
+ /**
262
+ * Get pending review queue context for a project
263
+ * @param {Object} client - API client
264
+ * @param {Object} options - Optional query params
265
+ * @returns {Promise<Object>} Review queue context bundle
266
+ */
267
+ export async function getReviewQueueContext(client, options = {}) {
268
+ let endpoint = buildEndpointWithParams('/api/sdk/context/review-queue', options);
269
+ return client.request(endpoint);
270
+ }
271
+
213
272
  /**
214
273
  * Search for comparisons by name
215
274
  * @param {Object} client - API client
package/dist/api/index.js CHANGED
@@ -16,4 +16,4 @@ export { createApiClient, DEFAULT_API_URL } from './client.js';
16
16
  export { buildApiUrl, buildAuthHeader, buildBuildPayload, buildEndpointWithParams, buildQueryParams, buildRequestHeaders, buildScreenshotCheckObject, buildScreenshotPayload, buildShaCheckPayload, buildUserAgent, computeSha256, extractErrorBody, findScreenshotBySha, isAuthError, isRateLimited, parseApiError, partitionByShaExistence, shaExists, shouldRetryWithRefresh } from './core.js';
17
17
 
18
18
  // Endpoint functions
19
- export { checkShas, createBuild, finalizeBuild, finalizeParallelBuild, getBatchHotspots, getBuild, getBuilds, getComparison, getPreviewInfo, getScreenshotHotspots, getTddBaselines, getTokenContext, searchComparisons, updateBuildStatus, uploadPreviewZip, uploadScreenshot } from './endpoints.js';
19
+ export { checkShas, createBuild, finalizeBuild, finalizeParallelBuild, getBatchHotspots, getBuild, getBuildContext, getBuilds, getComparison, getComparisonContext, getPreviewInfo, getReviewQueueContext, getScreenshotContext, getScreenshotHotspots, getSimilarFingerprintContext, getTddBaselines, getTokenContext, searchComparisons, updateBuildStatus, uploadPreviewZip, uploadScreenshot } from './endpoints.js';
package/dist/auth/core.js CHANGED
@@ -162,7 +162,7 @@ export function buildTokenData(response, existingUser = null) {
162
162
  * @returns {{ valid: boolean, error: Error|null }}
163
163
  */
164
164
  export function validateTokens(auth, requiredField = 'accessToken') {
165
- if (!auth || !auth[requiredField]) {
165
+ if (!auth?.[requiredField]) {
166
166
  let message = requiredField === 'refreshToken' ? 'No refresh token found. Please run "vizzly login" first.' : 'No authentication token found. Please run "vizzly login" first.';
167
167
  return {
168
168
  valid: false,
@@ -9,13 +9,14 @@
9
9
 
10
10
  // Re-export token store utilities for convenience
11
11
  import { clearAuthTokens, getAuthTokens, saveAuthTokens } from '../utils/global-config.js';
12
- export { clearAuthTokens, getAuthTokens, saveAuthTokens };
12
+
13
13
  // HTTP client factory
14
14
  export { createAuthClient } from './client.js';
15
15
  // Core pure functions
16
16
  export { buildAuthHeader, buildAuthUserAgent, buildDevicePollPayload, buildLogoutPayload, buildRefreshPayload, buildRequestHeaders, buildTokenData, parseAuthError, parseAuthenticatedError, validateTokens } from './core.js';
17
17
  // Auth operations (take dependencies as parameters)
18
18
  export { completeDeviceFlow, initiateDeviceFlow, isAuthenticated, logout, pollDeviceAuthorization, refresh, whoami } from './operations.js';
19
+ export { clearAuthTokens, getAuthTokens, saveAuthTokens };
19
20
 
20
21
  /**
21
22
  * Create a token store adapter from global-config functions